From 809559e6dcc960d2f701c001bb37ad34ce581a9c Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Thu, 16 Apr 2026 21:19:25 +0200 Subject: [PATCH] Nym Wallet: deps updates, clipboard/updater/, icon, polishing... This rolls together desktop wallet hardening, UX polish, and operational fixes we have been carrying in the branch. The goal is safer defaults, less noisy background behaviour. Security - Tighten the Tauri CSP for production and keep connect-src aligned with real needs. - Add a safe URL opener path (allowlisted schemes / validation) so user-influenced links do not become an open redirect surface. - Replace unwrap usage in mixnet account flows with proper errors and propagation. - Add an internal threat-model note so future changes keep the same assumptions explicit. Clipboard and desktop - Add a window-level Tauri clipboard hook for normal inputs, with clear exclusions for currency fields, auth-sensitive paste, and opt-in replace-paste fields. - Wire an Edit menu (cut, copy, paste, select all) where it helps, and keep behaviour consistent with the hook. - Deduplicate clipboard field props and satisfy ESLint on optional paste handlers. Updater and vesting operations - Treat legacy static updater JSON (missing per-platform signatures) as a soft failure with a clear warning, instead of erroring the version check IPC - Cut vesting polling spam when the chain has no vesting account for the address, and map vesting "no account" to a dedicated BackendError for stable handling on the client. - Move high-frequency vesting query logs to debug and keep removed-query stubs at warn. Icons and first-run chrome - Regenerate macOS/Windows icon assets from a padded 1024 master so dock and switcher visual weight matches other apps; add a small script to regenerate from app-icon-source.png. - Default the app to dark mode, paint the HTML shell and webview background in the same dark base colour Housekeeping - Mock app context defaults to dark for consistency with the new baseline. Validation run locally where relevant: Rust check, TypeScript check, ESLint, and icon regeneration script smoke run. - Remove storybook and old webdriver tests too --- .github/workflows/ci-nym-wallet-frontend.yml | 34 + .github/workflows/ci-nym-wallet-storybook.yml | 53 - .../workflows/nightly-nym-wallet-build.yml | 2 +- .../workflows/publish-nym-wallet-macos.yml | 2 +- .../workflows/publish-nym-wallet-ubuntu.yml | 2 +- .../workflows/publish-nym-wallet-win11.yml | 2 +- nym-wallet/.eslintrc.json | 14 + nym-wallet/.nvmrc | 2 +- nym-wallet/.storybook/main.js | 60 - nym-wallet/.storybook/mocks/tauri/app.js | 8 - nym-wallet/.storybook/mocks/tauri/core.js | 8 - nym-wallet/.storybook/mocks/tauri/image.js | 4 - nym-wallet/.storybook/mocks/tauri/index.js | 113 - .../.storybook/mocks/tauri/webviewWindow.js | 10 - nym-wallet/.storybook/preview.js | 55 - nym-wallet/.storybook/storiesStyles.ts | 21 - nym-wallet/Cargo.lock | 682 +++-- nym-wallet/README.md | 15 +- nym-wallet/dist/.gitkeep | 0 nym-wallet/jest.config.cjs | 20 + nym-wallet/package.json | 53 +- nym-wallet/public/index.html | 19 + nym-wallet/public/log.html | 10 + nym-wallet/scripts/regenerate-tauri-icons.sh | 17 + nym-wallet/src-tauri/Capabilities.toml | 2 - nym-wallet/src-tauri/Cargo.toml | 15 +- .../capabilities/main-capability.json | 3 +- .../src-tauri/gen/schemas/acl-manifests.json | 2 +- .../src-tauri/gen/schemas/capabilities.json | 2 +- .../src-tauri/gen/schemas/desktop-schema.json | 92 +- .../src-tauri/gen/schemas/macOS-schema.json | 92 +- nym-wallet/src-tauri/icons/128x128.png | Bin 2827 -> 3679 bytes nym-wallet/src-tauri/icons/128x128@2x.png | Bin 0 -> 7923 bytes nym-wallet/src-tauri/icons/32x32.png | Bin 1008 -> 994 bytes nym-wallet/src-tauri/icons/64x64.png | Bin 0 -> 1984 bytes .../src-tauri/icons/app-icon-source.png | Bin 0 -> 62081 bytes nym-wallet/src-tauri/icons/icon.icns | Bin 410598 -> 130611 bytes nym-wallet/src-tauri/icons/icon.ico | Bin 67646 -> 14325 bytes nym-wallet/src-tauri/icons/icon.png | Bin 410598 -> 22435 bytes nym-wallet/src-tauri/icons/tray_icon.png | Bin 2827 -> 3582 bytes .../scripts/appimage-wayland-hook.sh | 22 + nym-wallet/src-tauri/src/error.rs | 49 +- nym-wallet/src-tauri/src/log.rs | 8 +- nym-wallet/src-tauri/src/main.rs | 44 +- nym-wallet/src-tauri/src/menu.rs | 43 +- .../src-tauri/src/operations/app/link.rs | 9 +- .../src-tauri/src/operations/app/version.rs | 27 +- .../src-tauri/src/operations/app/window.rs | 2 + .../src-tauri/src/operations/help/log.rs | 2 + .../src/operations/mixnet/account.rs | 65 +- .../src/operations/vesting/queries.rs | 84 +- nym-wallet/src-tauri/src/webview_theme.rs | 5 + nym-wallet/src-tauri/tauri.conf.json | 24 +- nym-wallet/src/api/networkOverview.test.ts | 51 + nym-wallet/src/api/networkOverview.ts | 175 ++ nym-wallet/src/common.tsx | 10 +- .../components/Accounts/AccountOverview.tsx | 3 +- .../Accounts/stories/Accounts.stories.tsx | 18 - nym-wallet/src/components/ActionsMenu.tsx | 2 +- nym-wallet/src/components/AppBar.tsx | 60 +- .../components/AppSessionLoadingOverlay.tsx | 68 + nym-wallet/src/components/Bonding/Bond.tsx | 78 +- .../Bonding/modals/NodeSettingsModal.tsx | 8 +- .../components/Bonding/modals/UnbondModal.tsx | 2 + .../Bonding/modals/UpdateBondAmountModal.tsx | 18 +- .../src/components/Buy/Tutorial.stories.tsx | 10 - nym-wallet/src/components/Buy/Tutorial.tsx | 50 +- .../components/Clipboard/ClipboardActions.tsx | 1 + .../Clipboard/ClipboardFormFields.tsx | 117 +- .../src/components/ConfirmTX.stories.tsx | 29 - nym-wallet/src/components/ConfirmTX.tsx | 49 +- .../components/CurrencyFormFieldWithPaste.tsx | 3 +- .../Delegation/DelegateBlocker.stories.tsx | 40 - .../components/Delegation/DelegateModal.tsx | 16 +- .../Delegation/DelegationActions.stories.tsx | 19 - .../components/Delegation/DelegationItem.tsx | 135 - .../Delegation/DelegationList.stories.tsx | 311 -- .../components/Delegation/DelegationList.tsx | 670 ++-- .../Delegation/DelegationModal.stories.tsx | 190 -- .../Delegation/Delegations.stories.tsx | 27 - .../src/components/Delegation/Delegations.tsx | 10 +- .../components/Delegation/Modals.stories.tsx | 144 - .../Delegation/PendingDelegationCard.tsx | 46 + .../Delegation/PendingDelegationItem.tsx | 49 - .../src/components/FeeWarning.stories.tsx | 20 - nym-wallet/src/components/LogViewer/index.tsx | 41 +- .../Login/LoginPasswordFormWrapper.tsx | 7 +- .../Login/MnemonicLoginFormWrapper.tsx | 3 +- nym-wallet/src/components/Mnemonic.tsx | 16 +- .../Modals/ConfirmationModal.stories.tsx | 70 - .../src/components/Modals/ModalListItem.tsx | 45 +- .../components/Modals/SimpleModal.stories.tsx | 260 -- .../src/components/Modals/SimpleModal.tsx | 108 +- nym-wallet/src/components/Nav.tsx | 307 +- .../components/NetworkSelector.stories.tsx | 20 - nym-wallet/src/components/NetworkSelector.tsx | 5 +- nym-wallet/src/components/NymCard.tsx | 53 +- .../src/components/Receive/ReceiveModal.tsx | 113 +- nym-wallet/src/components/Receive/index.tsx | 6 +- .../Rewards/RedeemModal.stories.tsx | 139 - .../Rewards/RewardsSummary.stories.tsx | 28 - .../components/Send/SendDetails.stories.tsx | 86 - .../src/components/Send/SendInputModal.tsx | 178 +- nym-wallet/src/components/Send/SendModal.tsx | 4 +- nym-wallet/src/components/Send/index.tsx | 6 +- .../src/components/Settings/AppVersion.tsx | 26 +- .../src/components/TauriLinkWrapper.tsx | 4 +- .../components/TokenPoolSelector.stories.tsx | 24 - .../src/components/headerControlPillSx.ts | 26 + nym-wallet/src/components/index.ts | 1 + nym-wallet/src/context/main.tsx | 76 +- nym-wallet/src/context/mocks/main.tsx | 23 +- nym-wallet/src/hooks/useGetBalance.ts | 169 +- nym-wallet/src/hooks/useNymUsdPrice.ts | 59 + .../src/hooks/useTauriTextEditingClipboard.ts | 91 + nym-wallet/src/layouts/AppLayout.tsx | 111 +- nym-wallet/src/layouts/AuthLayout.tsx | 107 +- nym-wallet/src/layouts/PageLayout.tsx | 23 +- nym-wallet/src/layouts/contentRail.ts | 17 + .../src/pages/auth/components/heading.tsx | 42 +- .../components/passwordStrength.stories.tsx | 61 - .../src/pages/auth/components/word-tiles.tsx | 121 +- .../src/pages/auth/pages/existing-account.tsx | 31 +- .../src/pages/auth/pages/forgot-password.tsx | 39 +- .../src/pages/auth/pages/signin-mnemonic.tsx | 2 +- .../src/pages/auth/pages/signin-password.tsx | 2 +- .../src/pages/auth/pages/verify-mnemonic.tsx | 18 +- nym-wallet/src/pages/auth/pages/welcome.tsx | 15 +- nym-wallet/src/pages/balance/Balance.tsx | 183 +- .../pages/balance/NetworkOverviewSection.tsx | 577 ++++ .../pages/balance/OverviewQuickActions.tsx | 96 + nym-wallet/src/pages/balance/Vesting.tsx | 5 +- nym-wallet/src/pages/balance/index.tsx | 26 +- nym-wallet/src/pages/bonding/Bonding.tsx | 233 +- .../src/pages/bonding/BondingPage.stories.tsx | 13 - .../bonding/node-settings/node-test/index.tsx | 10 +- nym-wallet/src/pages/buy/index.stories.tsx | 10 - nym-wallet/src/pages/buy/index.tsx | 10 +- .../delegation/DelegationPage.stories.tsx | 19 - nym-wallet/src/pages/delegation/index.tsx | 175 +- .../src/pages/settings/AdvancedSettings.tsx | 4 +- .../src/pages/settings/GeneralSettings.tsx | 18 +- .../src/pages/settings/SecuritySettings.tsx | 4 +- nym-wallet/src/pages/settings/Settings.tsx | 26 +- nym-wallet/src/pages/terminal/index.tsx | 221 +- .../src/stories/Introduction.stories.mdx | 7 - nym-wallet/src/stories/Playground.stories.tsx | 10 - nym-wallet/src/theme/theme.tsx | 147 +- nym-wallet/src/utils/qrCodeReact.tsx | 16 + nym-wallet/src/utils/safeOpenUrl.ts | 20 + nym-wallet/tsconfig.eslint.json | 2 - nym-wallet/tsconfig.json | 7 +- nym-wallet/webdriver/.gitignore | 5 - nym-wallet/webdriver/README.md | 86 - nym-wallet/webdriver/babel.config.js | 12 - .../common/constants/text-constants.js | 30 - .../webdriver/common/data/user-data.json | 9 - nym-wallet/webdriver/common/helpers/helper.js | 43 - nym-wallet/webdriver/package.json | 27 - .../webdriver/tests/pages/wallet.bond.js | 48 - .../webdriver/tests/pages/wallet.create.js | 24 - .../webdriver/tests/pages/wallet.delegate.js | 60 - .../webdriver/tests/pages/wallet.homepage.js | 42 - .../webdriver/tests/pages/wallet.login.js | 31 - .../webdriver/tests/pages/wallet.receive.js | 37 - .../webdriver/tests/pages/wallet.send.js | 52 - .../tests/pages/wallet.undelegate.js | 22 - .../specs/existinguser/test.wallet.bond.js | 54 - .../existinguser/test.wallet.delegate.js | 108 - .../specs/existinguser/test.wallet.home.js | 45 - .../specs/existinguser/test.wallet.receive.js | 28 - .../specs/existinguser/test.wallet.send.js | 55 - .../existinguser/test.wallet.undelegate.js | 32 - .../tests/specs/newuser/test.wallet.create.js | 39 - nym-wallet/webdriver/wdio.conf.cjs | 93 - nym-wallet/webdriver/yarn.lock | 2709 ----------------- nym-wallet/webpack.common.js | 21 + yarn.lock | 1581 ++++++++-- 178 files changed, 6221 insertions(+), 7721 deletions(-) create mode 100644 .github/workflows/ci-nym-wallet-frontend.yml delete mode 100644 .github/workflows/ci-nym-wallet-storybook.yml delete mode 100644 nym-wallet/.storybook/main.js delete mode 100644 nym-wallet/.storybook/mocks/tauri/app.js delete mode 100644 nym-wallet/.storybook/mocks/tauri/core.js delete mode 100644 nym-wallet/.storybook/mocks/tauri/image.js delete mode 100644 nym-wallet/.storybook/mocks/tauri/index.js delete mode 100644 nym-wallet/.storybook/mocks/tauri/webviewWindow.js delete mode 100644 nym-wallet/.storybook/preview.js delete mode 100644 nym-wallet/.storybook/storiesStyles.ts delete mode 100644 nym-wallet/dist/.gitkeep create mode 100644 nym-wallet/jest.config.cjs create mode 100755 nym-wallet/scripts/regenerate-tauri-icons.sh delete mode 100644 nym-wallet/src-tauri/Capabilities.toml create mode 100644 nym-wallet/src-tauri/icons/128x128@2x.png create mode 100644 nym-wallet/src-tauri/icons/64x64.png create mode 100644 nym-wallet/src-tauri/icons/app-icon-source.png create mode 100644 nym-wallet/src-tauri/scripts/appimage-wayland-hook.sh create mode 100644 nym-wallet/src-tauri/src/webview_theme.rs create mode 100644 nym-wallet/src/api/networkOverview.test.ts create mode 100644 nym-wallet/src/api/networkOverview.ts delete mode 100644 nym-wallet/src/components/Accounts/stories/Accounts.stories.tsx create mode 100644 nym-wallet/src/components/AppSessionLoadingOverlay.tsx delete mode 100644 nym-wallet/src/components/Buy/Tutorial.stories.tsx delete mode 100644 nym-wallet/src/components/ConfirmTX.stories.tsx delete mode 100644 nym-wallet/src/components/Delegation/DelegateBlocker.stories.tsx delete mode 100644 nym-wallet/src/components/Delegation/DelegationActions.stories.tsx delete mode 100644 nym-wallet/src/components/Delegation/DelegationItem.tsx delete mode 100644 nym-wallet/src/components/Delegation/DelegationList.stories.tsx delete mode 100644 nym-wallet/src/components/Delegation/DelegationModal.stories.tsx delete mode 100644 nym-wallet/src/components/Delegation/Delegations.stories.tsx delete mode 100644 nym-wallet/src/components/Delegation/Modals.stories.tsx create mode 100644 nym-wallet/src/components/Delegation/PendingDelegationCard.tsx delete mode 100644 nym-wallet/src/components/Delegation/PendingDelegationItem.tsx delete mode 100644 nym-wallet/src/components/FeeWarning.stories.tsx delete mode 100644 nym-wallet/src/components/Modals/ConfirmationModal.stories.tsx delete mode 100644 nym-wallet/src/components/Modals/SimpleModal.stories.tsx delete mode 100644 nym-wallet/src/components/NetworkSelector.stories.tsx delete mode 100644 nym-wallet/src/components/Rewards/RedeemModal.stories.tsx delete mode 100644 nym-wallet/src/components/Rewards/RewardsSummary.stories.tsx delete mode 100644 nym-wallet/src/components/Send/SendDetails.stories.tsx delete mode 100644 nym-wallet/src/components/TokenPoolSelector.stories.tsx create mode 100644 nym-wallet/src/components/headerControlPillSx.ts create mode 100644 nym-wallet/src/hooks/useNymUsdPrice.ts create mode 100644 nym-wallet/src/hooks/useTauriTextEditingClipboard.ts create mode 100644 nym-wallet/src/layouts/contentRail.ts delete mode 100644 nym-wallet/src/pages/auth/components/passwordStrength.stories.tsx create mode 100644 nym-wallet/src/pages/balance/NetworkOverviewSection.tsx create mode 100644 nym-wallet/src/pages/balance/OverviewQuickActions.tsx delete mode 100644 nym-wallet/src/pages/bonding/BondingPage.stories.tsx delete mode 100644 nym-wallet/src/pages/buy/index.stories.tsx delete mode 100644 nym-wallet/src/pages/delegation/DelegationPage.stories.tsx delete mode 100644 nym-wallet/src/stories/Introduction.stories.mdx delete mode 100644 nym-wallet/src/stories/Playground.stories.tsx create mode 100644 nym-wallet/src/utils/qrCodeReact.tsx create mode 100644 nym-wallet/src/utils/safeOpenUrl.ts delete mode 100644 nym-wallet/webdriver/.gitignore delete mode 100644 nym-wallet/webdriver/README.md delete mode 100644 nym-wallet/webdriver/babel.config.js delete mode 100644 nym-wallet/webdriver/common/constants/text-constants.js delete mode 100644 nym-wallet/webdriver/common/data/user-data.json delete mode 100644 nym-wallet/webdriver/common/helpers/helper.js delete mode 100644 nym-wallet/webdriver/package.json delete mode 100644 nym-wallet/webdriver/tests/pages/wallet.bond.js delete mode 100644 nym-wallet/webdriver/tests/pages/wallet.create.js delete mode 100644 nym-wallet/webdriver/tests/pages/wallet.delegate.js delete mode 100644 nym-wallet/webdriver/tests/pages/wallet.homepage.js delete mode 100644 nym-wallet/webdriver/tests/pages/wallet.login.js delete mode 100644 nym-wallet/webdriver/tests/pages/wallet.receive.js delete mode 100644 nym-wallet/webdriver/tests/pages/wallet.send.js delete mode 100644 nym-wallet/webdriver/tests/pages/wallet.undelegate.js delete mode 100644 nym-wallet/webdriver/tests/specs/existinguser/test.wallet.bond.js delete mode 100644 nym-wallet/webdriver/tests/specs/existinguser/test.wallet.delegate.js delete mode 100644 nym-wallet/webdriver/tests/specs/existinguser/test.wallet.home.js delete mode 100644 nym-wallet/webdriver/tests/specs/existinguser/test.wallet.receive.js delete mode 100644 nym-wallet/webdriver/tests/specs/existinguser/test.wallet.send.js delete mode 100644 nym-wallet/webdriver/tests/specs/existinguser/test.wallet.undelegate.js delete mode 100644 nym-wallet/webdriver/tests/specs/newuser/test.wallet.create.js delete mode 100644 nym-wallet/webdriver/wdio.conf.cjs delete mode 100644 nym-wallet/webdriver/yarn.lock diff --git a/.github/workflows/ci-nym-wallet-frontend.yml b/.github/workflows/ci-nym-wallet-frontend.yml new file mode 100644 index 0000000000..174c72448a --- /dev/null +++ b/.github/workflows/ci-nym-wallet-frontend.yml @@ -0,0 +1,34 @@ +name: ci-nym-wallet-frontend + +on: + pull_request: + paths: + - 'nym-wallet/**' + - '.github/workflows/ci-nym-wallet-frontend.yml' + +jobs: + types-lint: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-node@v4 + with: + node-version-file: nym-wallet/.nvmrc + cache: yarn + cache-dependency-path: yarn.lock + + - name: Install dependencies + run: yarn install --network-timeout 100000 + + - name: Build TypeScript packages (wallet depends on @nymproject/types, etc.) + run: yarn build:types + + - name: Typecheck nym-wallet + run: yarn --cwd nym-wallet tsc + + - name: Lint nym-wallet + run: yarn --cwd nym-wallet lint + + - name: Unit tests (nym-wallet) + run: yarn --cwd nym-wallet test diff --git a/.github/workflows/ci-nym-wallet-storybook.yml b/.github/workflows/ci-nym-wallet-storybook.yml deleted file mode 100644 index e669de8fff..0000000000 --- a/.github/workflows/ci-nym-wallet-storybook.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: ci-nym-wallet-storybook - -on: - pull_request: - paths: - - 'nym-wallet/**' - - '.github/workflows/ci-nym-wallet-storybook.yml' - -jobs: - build: - runs-on: arc-linux-latest-dind - steps: - - uses: actions/checkout@v6 - - - name: Install rsync - run: sudo apt-get install rsync - continue-on-error: true - - - uses: rlespinasse/github-slug-action@v3.x - - - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Setup yarn - run: npm install -g yarn - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ vars.REQUIRED_RUSTC_VERSION }} - - - name: Install wasm-pack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - - name: Build dependencies - run: yarn && yarn build - - - name: Build storybook - run: yarn storybook:build - working-directory: ./nym-wallet - - - name: Deploy branch to CI www (storybook) - continue-on-error: true - uses: easingthemes/ssh-deploy@main - env: - SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }} - ARGS: "-rltgoDzvO --delete" - SOURCE: "nym-wallet/storybook-static/" - REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }} - REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }} - TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/wallet-${{ env.GITHUB_REF_SLUG }} - EXCLUDE: "/dist/, /node_modules/" diff --git a/.github/workflows/nightly-nym-wallet-build.yml b/.github/workflows/nightly-nym-wallet-build.yml index b4d4f69a1f..9953385027 100644 --- a/.github/workflows/nightly-nym-wallet-build.yml +++ b/.github/workflows/nightly-nym-wallet-build.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v6 - name: Install Dependencies (Linux) - run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools + run: sudo apt-get update && sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools libsoup-3.0-dev libjavascriptcoregtk-4.1-dev if: matrix.os == 'ubuntu-22.04' - name: Install rust toolchain diff --git a/.github/workflows/publish-nym-wallet-macos.yml b/.github/workflows/publish-nym-wallet-macos.yml index 9498b6cf6a..1cd4887570 100644 --- a/.github/workflows/publish-nym-wallet-macos.yml +++ b/.github/workflows/publish-nym-wallet-macos.yml @@ -26,7 +26,7 @@ jobs: - name: Node uses: actions/setup-node@v4 with: - node-version: 21 + node-version: 22.13.0 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/publish-nym-wallet-ubuntu.yml b/.github/workflows/publish-nym-wallet-ubuntu.yml index 47867db838..4c92ea70bf 100644 --- a/.github/workflows/publish-nym-wallet-ubuntu.yml +++ b/.github/workflows/publish-nym-wallet-ubuntu.yml @@ -30,7 +30,7 @@ jobs: - name: Node uses: actions/setup-node@v4 with: - node-version: 21 + node-version: 22.13.0 cache: 'yarn' - name: Install Rust toolchain diff --git a/.github/workflows/publish-nym-wallet-win11.yml b/.github/workflows/publish-nym-wallet-win11.yml index 5c20e6c667..ab43e838b1 100644 --- a/.github/workflows/publish-nym-wallet-win11.yml +++ b/.github/workflows/publish-nym-wallet-win11.yml @@ -40,7 +40,7 @@ jobs: - name: Node uses: actions/setup-node@v4 with: - node-version: 21 + node-version: 22.13.0 - name: Download EV CodeSignTool from ssl.com working-directory: nym-wallet/src-tauri diff --git a/nym-wallet/.eslintrc.json b/nym-wallet/.eslintrc.json index 3ea1281af1..ffbd36d7b6 100644 --- a/nym-wallet/.eslintrc.json +++ b/nym-wallet/.eslintrc.json @@ -4,5 +4,19 @@ ], "parserOptions": { "project": "./tsconfig.eslint.json" + }, + "overrides": [ + { + "files": ["**/*.test.ts", "**/*.test.tsx"], + "env": { "jest": true } + } + ], + "rules": { + "@typescript-eslint/lines-between-class-members": "off", + "@typescript-eslint/naming-convention": "off", + "@typescript-eslint/no-implied-eval": "off", + "@typescript-eslint/no-throw-literal": "off", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/quotes": "off" } } diff --git a/nym-wallet/.nvmrc b/nym-wallet/.nvmrc index 25bf17fc5a..b087cc9af8 100644 --- a/nym-wallet/.nvmrc +++ b/nym-wallet/.nvmrc @@ -1 +1 @@ -18 \ No newline at end of file +22.13.0 \ No newline at end of file diff --git a/nym-wallet/.storybook/main.js b/nym-wallet/.storybook/main.js deleted file mode 100644 index c4a255d616..0000000000 --- a/nym-wallet/.storybook/main.js +++ /dev/null @@ -1,60 +0,0 @@ -/* eslint-disable no-param-reassign */ -const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); -const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); - -module.exports = { - stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], - addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'], - framework: '@storybook/react', - core: { - builder: 'webpack5', - }, - typescript: { reactDocgen: false }, - // webpackFinal: async (config, { configType }) => { - // // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION' - // // You can change the configuration based on that. - // // 'PRODUCTION' is used when building the static version of storybook. - webpackFinal: async (config) => { - config.module.rules.forEach((rule) => { - // look for SVG import rule and replace - // NOTE: the rule before modification is /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/ - if (rule.test?.toString().includes('svg')) { - rule.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/; - } - }); - - // handle asset loading with this - config.module.rules.unshift({ - test: /\.svg(\?.*)?$/i, - issuer: /\.[jt]sx?$/, - use: ['@svgr/webpack'], - }); - - config.resolve.extensions = ['.tsx', '.ts', '.js']; - config.resolve.plugins = [new TsconfigPathsPlugin()]; - - config.plugins.push( - new ForkTsCheckerWebpackPlugin({ - typescript: { - mode: 'write-references', - diagnosticOptions: { - semantic: true, - syntactic: true, - }, - }, - }), - ); - - if (!config.resolve.alias) { - config.resolve.alias = {}; - } - - config.resolve.alias['@tauri-apps/api'] = `${__dirname}/mocks/tauri`; - - // Return the altered config - return config; - }, - features: { - emotionAlias: false, - }, -}; diff --git a/nym-wallet/.storybook/mocks/tauri/app.js b/nym-wallet/.storybook/mocks/tauri/app.js deleted file mode 100644 index 83d80d7037..0000000000 --- a/nym-wallet/.storybook/mocks/tauri/app.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/app), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ - -module.exports = { - getVersion: () => undefined, -}; diff --git a/nym-wallet/.storybook/mocks/tauri/core.js b/nym-wallet/.storybook/mocks/tauri/core.js deleted file mode 100644 index 2a81b7c205..0000000000 --- a/nym-wallet/.storybook/mocks/tauri/core.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/app), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ - -module.exports = { - invoke: () => undefined, -} \ No newline at end of file diff --git a/nym-wallet/.storybook/mocks/tauri/image.js b/nym-wallet/.storybook/mocks/tauri/image.js deleted file mode 100644 index b3368e223a..0000000000 --- a/nym-wallet/.storybook/mocks/tauri/image.js +++ /dev/null @@ -1,4 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/app), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ \ No newline at end of file diff --git a/nym-wallet/.storybook/mocks/tauri/index.js b/nym-wallet/.storybook/mocks/tauri/index.js deleted file mode 100644 index 3a40c8158f..0000000000 --- a/nym-wallet/.storybook/mocks/tauri/index.js +++ /dev/null @@ -1,113 +0,0 @@ -const delegations = [ - { - mix_id: 1234, - node_identity: 'FiojKW7oY9WQmLCiYAsCA21tpowZHS6zcUoyYm319p6Z', - delegated_on_iso_datetime: new Date(2021, 1, 1).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - amount: { amount: '10', denom: 'nym' }, - owner: '', - block_height: BigInt(100), - cost_params: { - profit_margin_percent: '0.04', - interval_operating_cost: { - amount: '20', - denom: 'nym', - }, - }, - stake_saturation: '0.2', - avg_uptime_percent: 0.5, - accumulated_by_delegates: { amount: '0', denom: 'nym' }, - accumulated_by_operator: { amount: '0', denom: 'nym' }, - uses_vesting_contract_tokens: false, - pending_events: [], - mixnode_is_unbonding: false, - errors: null, - }, - { - mix_id: 5678, - node_identity: 'DT8S942S8AQs2zKHS9SVo1GyHmuca3pfL2uLhLksJ3D8', - unclaimed_rewards: { amount: '0.1', denom: 'nym' }, - amount: { amount: '100', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.5', - avg_uptime_percent: 0.1, - cost_params: { - profit_margin_percent: '0.04', - interval_operating_cost: { - amount: '60', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '0', denom: 'nym' }, - accumulated_by_operator: { amount: '0', denom: 'nym' }, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: false, - errors: null, - }, -]; - - -/** - * This is a mock for Tauri's API package (@tauri-apps/api), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ -module.exports = { - invoke: (operation, args) => { - switch (operation) { - case 'get_balance': { - return { - amount: { - amount: '100', - denom: 'nymt', - }, - printable_balance: '100 NYMT', - }; - } - case 'delegate_to_mixnode': { - return { - logs_json: '[]', - data_json: '{}', - transaction_hash: '12345', - }; - } - case 'simulate_send': { - return { - amount: { - amount: '0.01', - denom: 'nym', - }, - }; - } - case 'get_delegation_summary': { - return { - delegations, - total_delegations: { - amount: '1000', - denom: 'nymt', - }, - total_rewards: { - amount: '42', - denom: 'nymt', - }, - }; - } - case 'get_pending_delegation_events' : { - return []; - } - case 'migrate_vested_delegations': { - delegations[1].uses_vesting_contract_tokens = false; - return {}; - } - } - - console.error( - `Tauri cannot be used in Storybook. The operation requested was "${operation}". You can add mock responses to "nym_wallet/.storybook/mocks/tauri.js" if you need. The default response is "void".`, - ); - return new Promise((resolve, reject) => { - reject(new Error(`Tauri operation ${operation} not available in storybook.`)); - }); - }, -}; diff --git a/nym-wallet/.storybook/mocks/tauri/webviewWindow.js b/nym-wallet/.storybook/mocks/tauri/webviewWindow.js deleted file mode 100644 index 9f0afeeebd..0000000000 --- a/nym-wallet/.storybook/mocks/tauri/webviewWindow.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * This is a mock for Tauri's API package (@tauri-apps/api/window), to prevent stories from being excluded, because they either use - * or import dependencies that use Tauri. - */ - -module.exports = { - appWindow: { - maximize: () => undefined, - } -} diff --git a/nym-wallet/.storybook/preview.js b/nym-wallet/.storybook/preview.js deleted file mode 100644 index 59e2502ebc..0000000000 --- a/nym-wallet/.storybook/preview.js +++ /dev/null @@ -1,55 +0,0 @@ -import { NymWalletThemeWithMode } from '../src/theme/NymWalletTheme'; -import { Box } from '@mui/material'; - -export const parameters = { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, -}; - -const withThemeProvider = (Story, context) => ( -
-
- - theme.palette.background.default, - color: 'text.primary', - }} - > - - - -

Light mode

-
-
-
-
- - theme.palette.background.default, - color: 'text.primary', - }} - > - - - -

Dark mode

-
-
-
-
-); - -export const decorators = [withThemeProvider]; diff --git a/nym-wallet/.storybook/storiesStyles.ts b/nym-wallet/.storybook/storiesStyles.ts deleted file mode 100644 index 172dbbfe2a..0000000000 --- a/nym-wallet/.storybook/storiesStyles.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Theme } from '@mui/material/styles'; - -export const backDropStyles = (theme: Theme) => { - const { mode } = theme.palette; - return { - style: { - left: mode === 'light' ? '0' : '50%', - width: '50%', - }, - }; -}; - -export const modalStyles = (theme: Theme) => { - const { mode } = theme.palette; - return { left: mode === 'light' ? '25%' : '75%' }; -}; - -export const dialogStyles = (theme: Theme) => { - const { mode } = theme.palette; - return { left: mode === 'light' ? '-50%' : '50%' }; -}; diff --git a/nym-wallet/Cargo.lock b/nym-wallet/Cargo.lock index da28ebe4e7..fce576f5d8 100644 --- a/nym-wallet/Cargo.lock +++ b/nym-wallet/Cargo.lock @@ -32,7 +32,6 @@ dependencies = [ "nym-vesting-contract-common", "nym-wallet-types", "once_cell", - "open", "plist", "pretty_env_logger", "rand_chacha 0.3.1", @@ -427,7 +426,7 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64" dependencies = [ - "brotli", + "brotli 7.0.0", "flate2", "futures-core", "memchr", @@ -450,17 +449,6 @@ dependencies = [ "slab", ] -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - [[package]] name = "async-io" version = "2.4.0" @@ -835,7 +823,18 @@ checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", + "brotli-decompressor 4.0.2", +] + +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 5.0.0", ] [[package]] @@ -848,6 +847,16 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bs58" version = "0.5.1" @@ -1043,7 +1052,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -1468,15 +1477,15 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.27.2" +version = "0.29.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa 0.4.8", + "itoa", "matches", - "phf 0.8.0", + "phf 0.10.1", "proc-macro2", "quote", "smallvec", @@ -1853,9 +1862,9 @@ dependencies = [ [[package]] name = "dlopen2" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" dependencies = [ "dlopen2_derive", "libc", @@ -3081,16 +3090,14 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", + "match_token", ] [[package]] @@ -3101,7 +3108,7 @@ checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", - "itoa 1.0.15", + "itoa", ] [[package]] @@ -3112,7 +3119,7 @@ checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.15", + "itoa", ] [[package]] @@ -3201,7 +3208,7 @@ dependencies = [ "http-body 0.4.6", "httparse", "httpdate", - "itoa 1.0.15", + "itoa", "pin-project-lite", "socket2 0.5.9", "tokio", @@ -3224,7 +3231,7 @@ dependencies = [ "http 1.3.1", "http-body 1.0.1", "httparse", - "itoa 1.0.15", + "itoa", "pin-project-lite", "pin-utils", "smallvec", @@ -3261,7 +3268,6 @@ dependencies = [ "tokio", "tokio-rustls 0.26.2", "tower-service", - "webpki-roots", ] [[package]] @@ -3315,7 +3321,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.60.1", ] [[package]] @@ -3329,9 +3335,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", "png", @@ -3643,12 +3649,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.15" @@ -3718,10 +3718,12 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -3801,14 +3803,13 @@ dependencies = [ [[package]] name = "kuchikiki" -version = "0.8.2" +version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 1.9.3", - "matches", + "indexmap 2.8.0", "selectors", ] @@ -3847,9 +3848,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.175" +version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] name = "libcrux-aesgcm" @@ -4120,9 +4121,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -4170,18 +4171,29 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf 0.11.3", + "phf_codegen 0.11.3", "string_cache", "string_cache_codegen", "tendril", ] +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "matchers" version = "0.2.0" @@ -4272,9 +4284,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.16.1" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" dependencies = [ "crossbeam-channel", "dpi", @@ -4288,7 +4300,7 @@ dependencies = [ "png", "serde", "thiserror 2.0.12", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4344,19 +4356,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "nodrop" version = "0.1.14" @@ -5070,6 +5069,7 @@ dependencies = [ "serde", "thiserror 2.0.12", "x25519-dalek", + "zeroize", ] [[package]] @@ -5106,36 +5106,9 @@ checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" dependencies = [ "bitflags 2.9.0", "block2 0.6.0", - "libc", "objc2 0.6.0", - "objc2-cloud-kit", - "objc2-core-data", "objc2-core-foundation", "objc2-core-graphics", - "objc2-core-image", - "objc2-foundation 0.3.0", - "objc2-quartz-core 0.3.0", -] - -[[package]] -name = "objc2-cloud-kit" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c" -dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", -] - -[[package]] -name = "objc2-core-data" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4" -dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", "objc2-foundation 0.3.0", ] @@ -5161,16 +5134,6 @@ dependencies = [ "objc2-io-surface", ] -[[package]] -name = "objc2-core-image" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56" -dependencies = [ - "objc2 0.6.0", - "objc2-foundation 0.3.0", -] - [[package]] name = "objc2-encode" version = "4.1.0" @@ -5259,17 +5222,6 @@ dependencies = [ "objc2-metal", ] -[[package]] -name = "objc2-quartz-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" -dependencies = [ - "bitflags 2.9.0", - "objc2 0.6.0", - "objc2-foundation 0.3.0", -] - [[package]] name = "objc2-ui-kit" version = "0.3.0" @@ -5653,9 +5605,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros 0.8.0", "phf_shared 0.8.0", - "proc-macro-hack", ] [[package]] @@ -5664,7 +5614,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ + "phf_macros 0.10.0", "phf_shared 0.10.0", + "proc-macro-hack", ] [[package]] @@ -5689,12 +5641,12 @@ dependencies = [ [[package]] name = "phf_codegen" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -5729,12 +5681,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", + "phf_generator 0.10.0", + "phf_shared 0.10.0", "proc-macro-hack", "proc-macro2", "quote", @@ -6426,10 +6378,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "quinn", - "rustls 0.23.37", "rustls-pemfile 2.2.0", - "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -6437,16 +6386,12 @@ dependencies = [ "system-configuration 0.6.1", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.2", - "tokio-util", "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", "web-sys", - "webpki-roots", "windows-registry", ] @@ -6459,6 +6404,7 @@ dependencies = [ "base64 0.22.1", "bytes", "futures-core", + "futures-util", "http 1.3.1", "http-body 1.0.1", "http-body-util", @@ -6478,12 +6424,14 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-rustls 0.26.2", + "tokio-util", "tower", "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", ] @@ -6617,14 +6565,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys 0.12.1", "windows-sys 0.59.0", ] @@ -6907,22 +6855,20 @@ dependencies = [ [[package]] name = "selectors" -version = "0.22.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", "cssparser", "derive_more 0.99.19", "fxhash", "log", - "matches", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", "servo_arc", "smallvec", - "thin-slice", ] [[package]] @@ -7000,7 +6946,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", @@ -7035,6 +6981,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -7042,7 +6997,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] @@ -7084,7 +7039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap 2.8.0", - "itoa 1.0.15", + "itoa", "ryu", "serde", "unsafe-libyaml", @@ -7112,9 +7067,9 @@ dependencies = [ [[package]] name = "serialize-to-javascript" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" dependencies = [ "serde", "serde_json", @@ -7123,20 +7078,20 @@ dependencies = [ [[package]] name = "serialize-to-javascript-impl" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] name = "servo_arc" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" dependencies = [ "nodrop", "stable_deref_trait", @@ -7277,7 +7232,7 @@ dependencies = [ "log", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-quartz-core 0.2.2", + "objc2-quartz-core", "raw-window-handle", "redox_syscall", "wasm-bindgen", @@ -7568,11 +7523,12 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tao" -version = "0.32.8" +version = "0.34.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1" +checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7" dependencies = [ "bitflags 2.9.0", + "block2 0.6.0", "core-foundation 0.10.0", "core-graphics", "crossbeam-channel", @@ -7599,8 +7555,8 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.3", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -7641,17 +7597,17 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.4.1" +version = "2.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d08db1ff9e011e04014e737ec022610d756c0eae0b3b3a9037bccaf3003173a" +checksum = "da77cc00fb9028caf5b5d4650f75e31f1ef3693459dfca7f7e506d1ecef0ba2d" dependencies = [ "anyhow", "bytes", + "cookie", "dirs 6.0.0", "dunce", "embed_plist", - "futures-util", - "getrandom 0.2.15", + "getrandom 0.3.3", "glob", "gtk", "heck 0.5.0", @@ -7664,10 +7620,12 @@ dependencies = [ "objc2 0.6.0", "objc2-app-kit", "objc2-foundation 0.3.0", + "objc2-ui-kit", + "objc2-web-kit", "percent-encoding", "plist", "raw-window-handle", - "reqwest 0.12.15", + "reqwest 0.13.2", "serde", "serde_json", "serde_repr", @@ -7682,18 +7640,17 @@ dependencies = [ "tokio", "tray-icon", "url", - "urlpattern", "webkit2gtk", "webview2-com", "window-vibrancy", - "windows 0.60.0", + "windows 0.61.3", ] [[package]] name = "tauri-build" -version = "2.1.1" +version = "2.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd20e4661c2cce65343319e6e8da256958f5af958cafc47c0d0af66a55dcd17" +checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d" dependencies = [ "anyhow", "cargo_toml", @@ -7707,18 +7664,18 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.8.22", + "toml 0.9.5", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.1.1" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4" +checksum = "d4a24476afd977c5d5d169f72425868613d82747916dd29e0a357c84c4bd6d29" dependencies = [ "base64 0.22.1", - "brotli", + "brotli 8.0.2", "ico", "json-patch", "plist", @@ -7740,9 +7697,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.1.1" +version = "2.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d402813d3b9c773a0fa58697c457c771f10e735498fdcb7b343264d18e5a601f" +checksum = "d39b349a98dadaffebb73f0a40dcd1f23c999211e5a2e744403db384d0c33de7" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -7754,9 +7711,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.1.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4190775d6ff73fe66d9af44c012739a2659720efd9c0e1e56a918678038699d" +checksum = "ddde7d51c907b940fb573006cdda9a642d6a7c8153657e88f8a5c3c9290cd4aa" dependencies = [ "anyhow", "glob", @@ -7765,15 +7722,15 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.8.22", + "toml 0.9.5", "walkdir", ] [[package]] name = "tauri-plugin-clipboard-manager" -version = "2.2.2" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab4cb42fdf745229b768802e9180920a4be63122cf87ed1c879103f7609d98e" +checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf" dependencies = [ "arboard", "log", @@ -7786,9 +7743,9 @@ dependencies = [ [[package]] name = "tauri-plugin-opener" -version = "2.2.6" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fdc6cb608e04b7d2b6d1f21e9444ad49245f6d03465ba53323d692d1ceb1a30" +checksum = "fc624469b06f59f5a29f874bbc61a2ed737c0f9c23ef09855a292c389c42e83f" dependencies = [ "dunce", "glob", @@ -7802,15 +7759,15 @@ dependencies = [ "tauri-plugin", "thiserror 2.0.12", "url", - "windows 0.60.0", + "windows 0.61.3", "zbus", ] [[package]] name = "tauri-plugin-process" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da5888533e802b6206b9685091f8714aa1f5266dc80051a82388449558b773" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" dependencies = [ "tauri", "tauri-plugin", @@ -7818,9 +7775,9 @@ dependencies = [ [[package]] name = "tauri-plugin-shell" -version = "2.2.1" +version = "2.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d5eb3368b959937ad2aeaf6ef9a8f5d11e01ffe03629d3530707bbcb27ff5d" +checksum = "8457dbf9e2bab1edd8df22bb2c20857a59a9868e79cb3eac5ed639eec4d0c73b" dependencies = [ "encoding_rs", "log", @@ -7839,9 +7796,9 @@ dependencies = [ [[package]] name = "tauri-plugin-updater" -version = "2.7.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82da763248e635d60ee4aed56c862290e523acc838d83097171f9a544708387" +checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af" dependencies = [ "base64 0.22.1", "dirs 6.0.0", @@ -7853,7 +7810,8 @@ dependencies = [ "minisign-verify", "osakit", "percent-encoding", - "reqwest 0.12.15", + "reqwest 0.13.2", + "rustls 0.23.37", "semver", "serde", "serde_json", @@ -7865,35 +7823,40 @@ dependencies = [ "time", "tokio", "url", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "zip", ] [[package]] name = "tauri-runtime" -version = "2.5.1" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ada7ac2f9276f09b8c3afffd3215fd5d9bff23c22df8a7c70e7ef67cacd532" +checksum = "2826d79a3297ed08cd6ea7f412644ef58e32969504bc4fbd8d7dbeabc4445ea2" dependencies = [ "cookie", "dpi", "gtk", "http 1.3.1", "jni", + "objc2 0.6.0", + "objc2-ui-kit", + "objc2-web-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", "thiserror 2.0.12", "url", - "windows 0.60.0", + "webkit2gtk", + "webview2-com", + "windows 0.61.3", ] [[package]] name = "tauri-runtime-wry" -version = "2.5.1" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2e5842c57e154af43a20a49c7efee0ce2578c20b4c2bdf266852b422d2e421" +checksum = "e11ea2e6f801d275fdd890d6c9603736012742a1c33b96d0db788c9cdebf7f9e" dependencies = [ "gtk", "http 1.3.1", @@ -7901,7 +7864,6 @@ dependencies = [ "log", "objc2 0.6.0", "objc2-app-kit", - "objc2-foundation 0.3.0", "once_cell", "percent-encoding", "raw-window-handle", @@ -7912,18 +7874,18 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows 0.60.0", + "windows 0.61.3", "wry", ] [[package]] name = "tauri-utils" -version = "2.3.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447" +checksum = "219a1f983a2af3653f75b5747f76733b0da7ff03069c7a41901a5eb3ace4557d" dependencies = [ "anyhow", - "brotli", + "brotli 8.0.2", "cargo_metadata 0.19.2", "ctor", "dunce", @@ -7947,7 +7909,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.12", - "toml 0.8.22", + "toml 0.9.5", "url", "urlpattern", "uuid", @@ -7966,14 +7928,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.19.1" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.5", + "rustix 1.1.4", "windows-sys 0.59.0", ] @@ -8089,12 +8051,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - [[package]] name = "thiserror" version = "1.0.69" @@ -8163,7 +8119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", - "itoa 1.0.15", + "itoa", "libc", "num-conv", "num_threads", @@ -8325,11 +8281,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.8", + "toml_datetime 0.6.9", "toml_edit 0.22.26", ] +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "indexmap 2.8.0", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow 0.7.10", +] + [[package]] name = "toml_datetime" version = "0.6.9" @@ -8339,6 +8310,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -8346,7 +8326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.8.0", - "toml_datetime", + "toml_datetime 0.6.9", "winnow 0.5.40", ] @@ -8357,7 +8337,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ "indexmap 2.8.0", - "toml_datetime", + "toml_datetime 0.6.9", "winnow 0.5.40", ] @@ -8369,18 +8349,33 @@ checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap 2.8.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.8", + "toml_datetime 0.6.9", "toml_write", "winnow 0.7.10", ] +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.1", +] + [[package]] name = "toml_write" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + [[package]] name = "tower" version = "0.5.2" @@ -8494,9 +8489,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.20.0" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5" +checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" dependencies = [ "crossbeam-channel", "dirs 6.0.0", @@ -8511,7 +8506,7 @@ dependencies = [ "png", "serde", "thiserror 2.0.12", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -8874,48 +8869,32 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.100", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8923,31 +8902,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn 2.0.100", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-streams" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" dependencies = [ "futures-util", "js-sys", @@ -9042,9 +9021,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" dependencies = [ "js-sys", "wasm-bindgen", @@ -9062,9 +9041,9 @@ dependencies = [ [[package]] name = "webkit2gtk" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -9086,9 +9065,9 @@ dependencies = [ [[package]] name = "webkit2gtk-sys" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ "bitflags 1.3.2", "cairo-sys-rs", @@ -9124,23 +9103,23 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.36.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.60.0", - "windows-core 0.60.1", - "windows-implement 0.59.0", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-implement 0.60.0", "windows-interface 0.59.1", ] [[package]] name = "webview2-com-macros" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" dependencies = [ "proc-macro2", "quote", @@ -9149,13 +9128,13 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.36.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ "thiserror 2.0.12", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.3", + "windows-core 0.61.2", ] [[package]] @@ -9238,24 +9217,24 @@ dependencies = [ [[package]] name = "windows" -version = "0.60.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core 0.60.1", + "windows-core 0.61.2", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] [[package]] name = "windows-collections" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.60.1", + "windows-core 0.61.2", ] [[package]] @@ -9288,32 +9267,33 @@ checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" dependencies = [ "windows-implement 0.59.0", "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.2", + "windows-link 0.1.3", + "windows-result 0.3.4", "windows-strings 0.3.1", ] [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement 0.60.0", "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] name = "windows-future" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core 0.60.1", - "windows-link", + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", ] [[package]] @@ -9373,18 +9353,24 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.60.1", - "windows-link", + "windows-core 0.61.2", + "windows-link 0.1.3", ] [[package]] @@ -9393,9 +9379,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ - "windows-result 0.3.2", + "windows-result 0.3.4", "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-targets 0.53.5", ] [[package]] @@ -9409,11 +9395,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -9432,16 +9418,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -9480,6 +9466,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -9528,10 +9532,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -9542,13 +9547,22 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-version" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -9749,6 +9763,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" + [[package]] name = "winreg" version = "0.50.0" @@ -9808,14 +9828,15 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.50.5" +version = "0.54.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730" +checksum = "bb26159b420aa77684589a744ae9a9461a95395b848764ad12290a14d960a11a" dependencies = [ "base64 0.22.1", "block2 0.6.0", "cookie", "crossbeam-channel", + "dirs 6.0.0", "dpi", "dunce", "gdkx11", @@ -9844,8 +9865,8 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.3", + "windows-core 0.61.2", "windows-version", "x11-dl", ] @@ -9907,17 +9928,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" dependencies = [ "libc", - "rustix 1.0.5", -] - -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", + "rustix 1.1.4", ] [[package]] @@ -9946,13 +9957,12 @@ dependencies = [ [[package]] name = "zbus" -version = "5.5.0" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" +checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" dependencies = [ "async-broadcast", "async-executor", - "async-fs", "async-io", "async-lock", "async-process", @@ -9965,16 +9975,16 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix", + "libc", "ordered-stream", + "rustix 1.1.4", "serde", "serde_repr", - "static_assertions", "tracing", "uds_windows", - "windows-sys 0.59.0", + "uuid", + "windows-sys 0.61.2", "winnow 0.7.10", - "xdg-home", "zbus_macros", "zbus_names", "zvariant", @@ -9982,9 +9992,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.5.0" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" +checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", @@ -9997,12 +10007,11 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.2.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", - "static_assertions", "winnow 0.7.10", "zvariant", ] @@ -10113,13 +10122,12 @@ dependencies = [ [[package]] name = "zip" -version = "2.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "febbe83a485467affa75a75d28dc7494acd2f819e549536c47d46b3089b56164" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" dependencies = [ "arbitrary", "crc32fast", - "crossbeam-utils", "indexmap 2.8.0", "memchr", ] @@ -10154,14 +10162,13 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.4.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" dependencies = [ "endi", "enumflags2", "serde", - "static_assertions", "winnow 0.7.10", "zvariant_derive", "zvariant_utils", @@ -10169,9 +10176,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.4.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" dependencies = [ "proc-macro-crate 3.3.0", "proc-macro2", @@ -10182,14 +10189,13 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", "serde", - "static_assertions", "syn 2.0.100", "winnow 0.7.10", ] diff --git a/nym-wallet/README.md b/nym-wallet/README.md index b935f85118..2b699fef74 100644 --- a/nym-wallet/README.md +++ b/nym-wallet/README.md @@ -12,8 +12,8 @@ The Nym desktop wallet enables you to use the Nym network and take advantage of ## Installation prerequisites - Linux / Mac - `Yarn` -- `NodeJS >= v16.8.0` -- `Rust & cargo >= v1.56` +- `NodeJS >= v22.13.0` +- `Rust & cargo >= v1.85` ## Installation prerequisites - Windows @@ -66,6 +66,17 @@ yarn build ``` The output will compile different types of binaries dependent on your hardware / OS system. Once the binaries are built, they can be located as follows: +## Linux AppImage notes + +The wallet AppImage now ships with a Wayland-focused launch hook for modern Linux desktops. On Wayland sessions it: + +- prefers the system `libwayland-client.so` when one is available +- defaults `GDK_BACKEND=wayland` +- defaults `GDK_SCALE=1` +- defaults `GDK_DPI_SCALE=0.8` + +If you need to override this behavior for troubleshooting, set your own environment variables before launching the AppImage. + ## Admin mode The admin screens can be shown by setting the environment variable `ADMIN_ADDRESS`. You'll need to know the admin account address for the network you are using. diff --git a/nym-wallet/dist/.gitkeep b/nym-wallet/dist/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/nym-wallet/jest.config.cjs b/nym-wallet/jest.config.cjs new file mode 100644 index 0000000000..fd07e80128 --- /dev/null +++ b/nym-wallet/jest.config.cjs @@ -0,0 +1,20 @@ +/** @type {import('jest').Config} */ +module.exports = { + testEnvironment: 'node', + roots: ['/src'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: { + esModuleInterop: true, + module: 'commonjs', + }, + }, + ], + }, + moduleNameMapper: { + '^src/(.*)$': '/src/$1', + }, +}; diff --git a/nym-wallet/package.json b/nym-wallet/package.json index 134eab22f4..6b8d15d9bf 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -10,13 +10,11 @@ "lint": "eslint src", "lint:fix": "eslint src --fix", "prebuild": "yarn --cwd .. build", - "prestorybook": "yarn --cwd .. build", "prewebpack:dev": "yarn --cwd .. build", - "storybook": "start-storybook -p 6006", - "storybook:build": "build-storybook", "tauri:build": "yarn tauri build", "tauri:dev": "yarn tauri dev", "tauri:buildx86": "yarn tauri build --target x86_64-apple-darwin", + "test": "jest --config jest.config.cjs", "tsc": "tsc --noEmit true", "tsc:watch": "tsc --noEmit true --watch", "webpack:dev": "yarn webpack serve --config webpack.dev.js", @@ -26,21 +24,21 @@ "@babel/helper-simple-access": "^7.25.9", "@emotion/react": "^11.7.0", "@emotion/styled": "^11.6.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@hookform/resolvers": "^2.8.0", "@mui/icons-material": "^5.2.0", "@mui/material": "^5.2.2", - "@mui/styles": "^5.2.2", + "@mui/styles": "^5.18.0", "@mui/utils": "^5.7.0", - "@nymproject/mui-theme": "^1.0.0", "@nymproject/node-tester": "^1.3.1", "@nymproject/react": "^1.0.0", "@nymproject/types": "^1.0.0", - "@storybook/react": "^6.5.15", - "@tauri-apps/api": "^2.4.0", - "@tauri-apps/plugin-clipboard-manager": "^2.2.2", - "@tauri-apps/plugin-opener": "^2.2.6", - "@tauri-apps/plugin-shell": "^2.2.1", - "@tauri-apps/plugin-updater": "^2.0.0", + "@tauri-apps/api": "^2.10.1", + "@tauri-apps/plugin-clipboard-manager": "^2.3.2", + "@tauri-apps/plugin-opener": "^2.5.3", + "@tauri-apps/plugin-process": "^2.3.1", + "@tauri-apps/plugin-shell": "^2.3.5", + "@tauri-apps/plugin-updater": "^2.10.1", "@tauri-apps/tauri-forage": "^1.0.0-beta.2", "big.js": "^6.2.1", "bs58": "^4.0.1", @@ -51,8 +49,8 @@ "notistack": "^2.0.3", "npm-run-all": "^4.1.5", "qrcode.react": "^1.0.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", "react-error-boundary": "^3.1.3", "react-hook-form": "^7.14.2", "react-router-dom": "6", @@ -72,31 +70,31 @@ "@babel/preset-typescript": "^7.15.0", "@nymproject/eslint-config-react-typescript": "^1.0.0", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", - "@storybook/react": "^6.5.15", "@svgr/webpack": "^6.1.1", - "@tauri-apps/cli": "^2.4.0", - "@testing-library/jest-dom": "^5.14.1", - "@testing-library/react": "^12.0.0", + "@tauri-apps/cli": "^2.10.1", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", "@types/big.js": "^6.1.6", "@types/bs58": "^4.0.1", - "@types/jest": "^27.0.1", + "@types/jest": "^29.5.14", "@types/minimatch": "5.1.2", - "@types/node": "^16.7.13", + "@types/node": "^22.15.29", "@types/qrcode.react": "^1.0.2", - "@types/react": "^18.0.26", - "@types/react-dom": "^18.0.10", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "@types/semver": "^7.3.8", "@types/uuid": "^8.3.4", "@types/zxcvbn": "^4.4.1", - "@typescript-eslint/eslint-plugin": "^5.13.0", - "@typescript-eslint/parser": "^5.13.0", + "@typescript-eslint/eslint-plugin": "^8.56.1", + "@typescript-eslint/parser": "^8.56.1", "babel-loader": "^8.3.0", "babel-plugin-root-import": "^6.6.0", "clean-webpack-plugin": "^4.0.0", "css-loader": "^6.7.3", "css-minimizer-webpack-plugin": "^3.0.2", "dotenv-webpack": "^7.0.3", - "eslint": "^9.26.0", + "eslint": "^8.57.1", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^16.1.0", "eslint-config-prettier": "^8.5.0", @@ -107,13 +105,12 @@ "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-react": "^7.29.2", "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-storybook": "^0.5.12", "favicons": "^7.0.2", "favicons-webpack-plugin": "^5.0.2", "file-loader": "^6.2.0", "fork-ts-checker-webpack-plugin": "^7.2.1", "html-webpack-plugin": "^5.3.2", - "jest": "^27.1.0", + "jest": "^30.3.0", "mini-css-extract-plugin": "^2.2.2", "npm-run-all": "^4.1.5", "prettier": "^2.8.7", @@ -121,10 +118,10 @@ "react-refresh-typescript": "^2.0.2", "style-loader": "^3.3.1", "thread-loader": "^3.0.4", - "ts-jest": "^27.0.5", + "ts-jest": "^29.4.9", "ts-loader": "^9.4.2", "tsconfig-paths-webpack-plugin": "^3.5.2", - "typescript": "^4.6.2", + "typescript": "^5.9.3", "url-loader": "^4.1.1", "webpack": "^5.75.0", "webpack-cli": "^4.8.0", diff --git a/nym-wallet/public/index.html b/nym-wallet/public/index.html index af101025cb..7e4a73b66f 100644 --- a/nym-wallet/public/index.html +++ b/nym-wallet/public/index.html @@ -4,6 +4,25 @@ Nym Wallet +
diff --git a/nym-wallet/public/log.html b/nym-wallet/public/log.html index 5ced2243f1..cfa9c797d8 100644 --- a/nym-wallet/public/log.html +++ b/nym-wallet/public/log.html @@ -4,6 +4,16 @@ Nym Wallet Logs +
diff --git a/nym-wallet/scripts/regenerate-tauri-icons.sh b/nym-wallet/scripts/regenerate-tauri-icons.sh new file mode 100755 index 0000000000..78d579257d --- /dev/null +++ b/nym-wallet/scripts/regenerate-tauri-icons.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Regenerate macOS/Windows icon bundles from the 1024x1024 master in src-tauri/icons/. +# Master file: app-icon-source.png (padded per Apple-style safe zone). Edit that asset, then run: +# ./scripts/regenerate-tauri-icons.sh +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +SRC="$ROOT/src-tauri/icons/app-icon-source.png" +yarn --cwd "$ROOT" tauri icon "$SRC" -o "$ROOT/src-tauri/icons" +rm -rf "$ROOT/src-tauri/icons/android" "$ROOT/src-tauri/icons/ios" +rm -f "$ROOT/src-tauri/icons"/Square*.png "$ROOT/src-tauri/icons/StoreLogo.png" +python3 - <","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}},"updater":{"default_permission":{"identifier":"default","description":"This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n","permissions":["allow-check","allow-download","allow-install","allow-download-and-install"]},"permissions":{"allow-check":{"identifier":"allow-check","description":"Enables the check command without any pre-configured scope.","commands":{"allow":["check"],"deny":[]}},"allow-download":{"identifier":"allow-download","description":"Enables the download command without any pre-configured scope.","commands":{"allow":["download"],"deny":[]}},"allow-download-and-install":{"identifier":"allow-download-and-install","description":"Enables the download_and_install command without any pre-configured scope.","commands":{"allow":["download_and_install"],"deny":[]}},"allow-install":{"identifier":"allow-install","description":"Enables the install command without any pre-configured scope.","commands":{"allow":["install"],"deny":[]}},"deny-check":{"identifier":"deny-check","description":"Denies the check command without any pre-configured scope.","commands":{"allow":[],"deny":["check"]}},"deny-download":{"identifier":"deny-download","description":"Denies the download command without any pre-configured scope.","commands":{"allow":[],"deny":["download"]}},"deny-download-and-install":{"identifier":"deny-download-and-install","description":"Denies the download_and_install command without any pre-configured scope.","commands":{"allow":[],"deny":["download_and_install"]}},"deny-install":{"identifier":"deny-install","description":"Denies the install command without any pre-configured scope.","commands":{"allow":[],"deny":["install"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file +{"clipboard-manager":{"default_permission":{"identifier":"default","description":"No features are enabled by default, as we believe\nthe clipboard can be inherently dangerous and it is \napplication specific if read and/or write access is needed.\n\nClipboard interaction needs to be explicitly enabled.\n","permissions":[]},"permissions":{"allow-clear":{"identifier":"allow-clear","description":"Enables the clear command without any pre-configured scope.","commands":{"allow":["clear"],"deny":[]}},"allow-read-image":{"identifier":"allow-read-image","description":"Enables the read_image command without any pre-configured scope.","commands":{"allow":["read_image"],"deny":[]}},"allow-read-text":{"identifier":"allow-read-text","description":"Enables the read_text command without any pre-configured scope.","commands":{"allow":["read_text"],"deny":[]}},"allow-write-html":{"identifier":"allow-write-html","description":"Enables the write_html command without any pre-configured scope.","commands":{"allow":["write_html"],"deny":[]}},"allow-write-image":{"identifier":"allow-write-image","description":"Enables the write_image command without any pre-configured scope.","commands":{"allow":["write_image"],"deny":[]}},"allow-write-text":{"identifier":"allow-write-text","description":"Enables the write_text command without any pre-configured scope.","commands":{"allow":["write_text"],"deny":[]}},"deny-clear":{"identifier":"deny-clear","description":"Denies the clear command without any pre-configured scope.","commands":{"allow":[],"deny":["clear"]}},"deny-read-image":{"identifier":"deny-read-image","description":"Denies the read_image command without any pre-configured scope.","commands":{"allow":[],"deny":["read_image"]}},"deny-read-text":{"identifier":"deny-read-text","description":"Denies the read_text command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text"]}},"deny-write-html":{"identifier":"deny-write-html","description":"Denies the write_html command without any pre-configured scope.","commands":{"allow":[],"deny":["write_html"]}},"deny-write-image":{"identifier":"deny-write-image","description":"Denies the write_image command without any pre-configured scope.","commands":{"allow":[],"deny":["write_image"]}},"deny-write-text":{"identifier":"deny-write-text","description":"Denies the write_text command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text"]}}},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"opener":{"default_permission":{"identifier":"default","description":"This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer","permissions":["allow-open-url","allow-reveal-item-in-dir","allow-default-urls"]},"permissions":{"allow-default-urls":{"identifier":"allow-default-urls","description":"This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"url":"mailto:*"},{"url":"tel:*"},{"url":"http://*"},{"url":"https://*"}]}},"allow-open-path":{"identifier":"allow-open-path","description":"Enables the open_path command without any pre-configured scope.","commands":{"allow":["open_path"],"deny":[]}},"allow-open-url":{"identifier":"allow-open-url","description":"Enables the open_url command without any pre-configured scope.","commands":{"allow":["open_url"],"deny":[]}},"allow-reveal-item-in-dir":{"identifier":"allow-reveal-item-in-dir","description":"Enables the reveal_item_in_dir command without any pre-configured scope.","commands":{"allow":["reveal_item_in_dir"],"deny":[]}},"deny-open-path":{"identifier":"deny-open-path","description":"Denies the open_path command without any pre-configured scope.","commands":{"allow":[],"deny":["open_path"]}},"deny-open-url":{"identifier":"deny-open-url","description":"Denies the open_url command without any pre-configured scope.","commands":{"allow":[],"deny":["open_url"]}},"deny-reveal-item-in-dir":{"identifier":"deny-reveal-item-in-dir","description":"Denies the reveal_item_in_dir command without any pre-configured scope.","commands":{"allow":[],"deny":["reveal_item_in_dir"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"properties":{"app":{"allOf":[{"$ref":"#/definitions/Application"}],"description":"An application to open this url with, for example: firefox."},"url":{"description":"A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"","type":"string"}},"required":["url"],"type":"object"},{"properties":{"app":{"allOf":[{"$ref":"#/definitions/Application"}],"description":"An application to open this path with, for example: xdg-open."},"path":{"description":"A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"}},"required":["path"],"type":"object"}],"definitions":{"Application":{"anyOf":[{"description":"Open in default application.","type":"null"},{"description":"If true, allow open with any application.","type":"boolean"},{"description":"Allow specific application to open with.","type":"string"}],"description":"Opener scope application."}},"description":"Opener scope entry.","title":"OpenerScopeEntry"}},"process":{"default_permission":{"identifier":"default","description":"This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n","permissions":["allow-exit","allow-restart"]},"permissions":{"allow-exit":{"identifier":"allow-exit","description":"Enables the exit command without any pre-configured scope.","commands":{"allow":["exit"],"deny":[]}},"allow-restart":{"identifier":"allow-restart","description":"Enables the restart command without any pre-configured scope.","commands":{"allow":["restart"],"deny":[]}},"deny-exit":{"identifier":"deny-exit","description":"Denies the exit command without any pre-configured scope.","commands":{"allow":[],"deny":["exit"]}},"deny-restart":{"identifier":"deny-restart","description":"Denies the restart command without any pre-configured scope.","commands":{"allow":[],"deny":["restart"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}},"updater":{"default_permission":{"identifier":"default","description":"This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n","permissions":["allow-check","allow-download","allow-install","allow-download-and-install"]},"permissions":{"allow-check":{"identifier":"allow-check","description":"Enables the check command without any pre-configured scope.","commands":{"allow":["check"],"deny":[]}},"allow-download":{"identifier":"allow-download","description":"Enables the download command without any pre-configured scope.","commands":{"allow":["download"],"deny":[]}},"allow-download-and-install":{"identifier":"allow-download-and-install","description":"Enables the download_and_install command without any pre-configured scope.","commands":{"allow":["download_and_install"],"deny":[]}},"allow-install":{"identifier":"allow-install","description":"Enables the install command without any pre-configured scope.","commands":{"allow":["install"],"deny":[]}},"deny-check":{"identifier":"deny-check","description":"Denies the check command without any pre-configured scope.","commands":{"allow":[],"deny":["check"]}},"deny-download":{"identifier":"deny-download","description":"Denies the download command without any pre-configured scope.","commands":{"allow":[],"deny":["download"]}},"deny-download-and-install":{"identifier":"deny-download-and-install","description":"Denies the download_and_install command without any pre-configured scope.","commands":{"allow":[],"deny":["download_and_install"]}},"deny-install":{"identifier":"deny-install","description":"Denies the install command without any pre-configured scope.","commands":{"allow":[],"deny":["install"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/capabilities.json b/nym-wallet/src-tauri/gen/schemas/capabilities.json index a0a9361c59..e45d08051a 100644 --- a/nym-wallet/src-tauri/gen/schemas/capabilities.json +++ b/nym-wallet/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp","log"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","opener:allow-open-url","opener:allow-default-urls","core:window:allow-set-title","core:app:allow-version","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","updater:default","updater:allow-check","updater:allow-download","updater:allow-download-and-install","updater:allow-install","core:event:allow-listen","shell:allow-open"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file +{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp","log"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","opener:allow-open-url","opener:allow-default-urls","core:window:allow-set-title","core:app:allow-version","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","updater:default","updater:allow-check","updater:allow-download","updater:allow-download-and-install","updater:allow-install","core:event:allow-listen","shell:allow-open","process:default"],"platforms":["linux","macOS","windows"]}} \ No newline at end of file diff --git a/nym-wallet/src-tauri/gen/schemas/desktop-schema.json b/nym-wallet/src-tauri/gen/schemas/desktop-schema.json index 80f89170ce..1061cad2a0 100644 --- a/nym-wallet/src-tauri/gen/schemas/desktop-schema.json +++ b/nym-wallet/src-tauri/gen/schemas/desktop-schema.json @@ -37,7 +37,7 @@ ], "definitions": { "Capability": { - "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", "type": "object", "required": [ "identifier", @@ -49,7 +49,7 @@ "type": "string" }, "description": { - "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", "default": "", "type": "string" }, @@ -639,10 +639,10 @@ "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" }, { - "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`", "type": "string", "const": "core:app:default", - "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`" + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`" }, { "description": "Enables the app_hide command without any pre-configured scope.", @@ -656,6 +656,12 @@ "const": "core:app:allow-app-show", "markdownDescription": "Enables the app_show command without any pre-configured scope." }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, { "description": "Enables the default_window_icon command without any pre-configured scope.", "type": "string", @@ -680,18 +686,36 @@ "const": "core:app:allow-name", "markdownDescription": "Enables the name command without any pre-configured scope." }, + { + "description": "Enables the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-register-listener", + "markdownDescription": "Enables the register_listener command without any pre-configured scope." + }, { "description": "Enables the remove_data_store command without any pre-configured scope.", "type": "string", "const": "core:app:allow-remove-data-store", "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." }, + { + "description": "Enables the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-listener", + "markdownDescription": "Enables the remove_listener command without any pre-configured scope." + }, { "description": "Enables the set_app_theme command without any pre-configured scope.", "type": "string", "const": "core:app:allow-set-app-theme", "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." }, + { + "description": "Enables the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-dock-visibility", + "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." + }, { "description": "Enables the tauri_version command without any pre-configured scope.", "type": "string", @@ -716,6 +740,12 @@ "const": "core:app:deny-app-show", "markdownDescription": "Denies the app_show command without any pre-configured scope." }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, { "description": "Denies the default_window_icon command without any pre-configured scope.", "type": "string", @@ -740,18 +770,36 @@ "const": "core:app:deny-name", "markdownDescription": "Denies the name command without any pre-configured scope." }, + { + "description": "Denies the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-register-listener", + "markdownDescription": "Denies the register_listener command without any pre-configured scope." + }, { "description": "Denies the remove_data_store command without any pre-configured scope.", "type": "string", "const": "core:app:deny-remove-data-store", "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." }, + { + "description": "Denies the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-listener", + "markdownDescription": "Denies the remove_listener command without any pre-configured scope." + }, { "description": "Denies the set_app_theme command without any pre-configured scope.", "type": "string", "const": "core:app:deny-set-app-theme", "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." }, + { + "description": "Denies the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-dock-visibility", + "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." + }, { "description": "Denies the tauri_version command without any pre-configured scope.", "type": "string", @@ -1460,6 +1508,12 @@ "const": "core:webview:allow-reparent", "markdownDescription": "Enables the reparent command without any pre-configured scope." }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, { "description": "Enables the set_webview_background_color command without any pre-configured scope.", "type": "string", @@ -1562,6 +1616,12 @@ "const": "core:webview:deny-reparent", "markdownDescription": "Denies the reparent command without any pre-configured scope." }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, { "description": "Denies the set_webview_background_color command without any pre-configured scope.", "type": "string", @@ -1910,6 +1970,12 @@ "const": "core:window:allow-set-focus", "markdownDescription": "Enables the set_focus command without any pre-configured scope." }, + { + "description": "Enables the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focusable", + "markdownDescription": "Enables the set_focusable command without any pre-configured scope." + }, { "description": "Enables the set_fullscreen command without any pre-configured scope.", "type": "string", @@ -1982,6 +2048,12 @@ "const": "core:window:allow-set-shadow", "markdownDescription": "Enables the set_shadow command without any pre-configured scope." }, + { + "description": "Enables the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-simple-fullscreen", + "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope." + }, { "description": "Enables the set_size command without any pre-configured scope.", "type": "string", @@ -2354,6 +2426,12 @@ "const": "core:window:deny-set-focus", "markdownDescription": "Denies the set_focus command without any pre-configured scope." }, + { + "description": "Denies the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focusable", + "markdownDescription": "Denies the set_focusable command without any pre-configured scope." + }, { "description": "Denies the set_fullscreen command without any pre-configured scope.", "type": "string", @@ -2426,6 +2504,12 @@ "const": "core:window:deny-set-shadow", "markdownDescription": "Denies the set_shadow command without any pre-configured scope." }, + { + "description": "Denies the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-simple-fullscreen", + "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope." + }, { "description": "Denies the set_size command without any pre-configured scope.", "type": "string", diff --git a/nym-wallet/src-tauri/gen/schemas/macOS-schema.json b/nym-wallet/src-tauri/gen/schemas/macOS-schema.json index 80f89170ce..1061cad2a0 100644 --- a/nym-wallet/src-tauri/gen/schemas/macOS-schema.json +++ b/nym-wallet/src-tauri/gen/schemas/macOS-schema.json @@ -37,7 +37,7 @@ ], "definitions": { "Capability": { - "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", "type": "object", "required": [ "identifier", @@ -49,7 +49,7 @@ "type": "string" }, "description": { - "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", "default": "", "type": "string" }, @@ -639,10 +639,10 @@ "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" }, { - "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`", + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`", "type": "string", "const": "core:app:default", - "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`" + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`" }, { "description": "Enables the app_hide command without any pre-configured scope.", @@ -656,6 +656,12 @@ "const": "core:app:allow-app-show", "markdownDescription": "Enables the app_show command without any pre-configured scope." }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, { "description": "Enables the default_window_icon command without any pre-configured scope.", "type": "string", @@ -680,18 +686,36 @@ "const": "core:app:allow-name", "markdownDescription": "Enables the name command without any pre-configured scope." }, + { + "description": "Enables the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-register-listener", + "markdownDescription": "Enables the register_listener command without any pre-configured scope." + }, { "description": "Enables the remove_data_store command without any pre-configured scope.", "type": "string", "const": "core:app:allow-remove-data-store", "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." }, + { + "description": "Enables the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-listener", + "markdownDescription": "Enables the remove_listener command without any pre-configured scope." + }, { "description": "Enables the set_app_theme command without any pre-configured scope.", "type": "string", "const": "core:app:allow-set-app-theme", "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." }, + { + "description": "Enables the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-dock-visibility", + "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." + }, { "description": "Enables the tauri_version command without any pre-configured scope.", "type": "string", @@ -716,6 +740,12 @@ "const": "core:app:deny-app-show", "markdownDescription": "Denies the app_show command without any pre-configured scope." }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, { "description": "Denies the default_window_icon command without any pre-configured scope.", "type": "string", @@ -740,18 +770,36 @@ "const": "core:app:deny-name", "markdownDescription": "Denies the name command without any pre-configured scope." }, + { + "description": "Denies the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-register-listener", + "markdownDescription": "Denies the register_listener command without any pre-configured scope." + }, { "description": "Denies the remove_data_store command without any pre-configured scope.", "type": "string", "const": "core:app:deny-remove-data-store", "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." }, + { + "description": "Denies the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-listener", + "markdownDescription": "Denies the remove_listener command without any pre-configured scope." + }, { "description": "Denies the set_app_theme command without any pre-configured scope.", "type": "string", "const": "core:app:deny-set-app-theme", "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." }, + { + "description": "Denies the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-dock-visibility", + "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." + }, { "description": "Denies the tauri_version command without any pre-configured scope.", "type": "string", @@ -1460,6 +1508,12 @@ "const": "core:webview:allow-reparent", "markdownDescription": "Enables the reparent command without any pre-configured scope." }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, { "description": "Enables the set_webview_background_color command without any pre-configured scope.", "type": "string", @@ -1562,6 +1616,12 @@ "const": "core:webview:deny-reparent", "markdownDescription": "Denies the reparent command without any pre-configured scope." }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, { "description": "Denies the set_webview_background_color command without any pre-configured scope.", "type": "string", @@ -1910,6 +1970,12 @@ "const": "core:window:allow-set-focus", "markdownDescription": "Enables the set_focus command without any pre-configured scope." }, + { + "description": "Enables the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focusable", + "markdownDescription": "Enables the set_focusable command without any pre-configured scope." + }, { "description": "Enables the set_fullscreen command without any pre-configured scope.", "type": "string", @@ -1982,6 +2048,12 @@ "const": "core:window:allow-set-shadow", "markdownDescription": "Enables the set_shadow command without any pre-configured scope." }, + { + "description": "Enables the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-simple-fullscreen", + "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope." + }, { "description": "Enables the set_size command without any pre-configured scope.", "type": "string", @@ -2354,6 +2426,12 @@ "const": "core:window:deny-set-focus", "markdownDescription": "Denies the set_focus command without any pre-configured scope." }, + { + "description": "Denies the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focusable", + "markdownDescription": "Denies the set_focusable command without any pre-configured scope." + }, { "description": "Denies the set_fullscreen command without any pre-configured scope.", "type": "string", @@ -2426,6 +2504,12 @@ "const": "core:window:deny-set-shadow", "markdownDescription": "Denies the set_shadow command without any pre-configured scope." }, + { + "description": "Denies the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-simple-fullscreen", + "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope." + }, { "description": "Denies the set_size command without any pre-configured scope.", "type": "string", diff --git a/nym-wallet/src-tauri/icons/128x128.png b/nym-wallet/src-tauri/icons/128x128.png index c8c1b37414bd3270cbc0548ebf28c0193bef0353..412cadc6d7e7b903fdbb1a176d41c55f4f9d4613 100644 GIT binary patch delta 3679 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 GqW=dEG<)*^ literal 0 HcmV?d00001 diff --git a/nym-wallet/src-tauri/icons/32x32.png b/nym-wallet/src-tauri/icons/32x32.png index 76e4168cd73f1b42caa409533ac4530a870a294a..6549f3a6b87fed10bde1a47d82c7b8dd7992b1bc 100644 GIT binary patch delta 973 zcmV;;12X*Z2jT~iBYy*_Nklj;dENq;z*CB9Y2s1y0OZ)f5tcw2;FhhyHt7^C9|iIp(& zW4p^(mn1jWy>f$%D1;GNKBar)21WZ-5c!70FwbY=ihFeemnfkoRSgQ``%oI~<+*lf z*!9%p8u}kEGBT`rkHF&f5vV>1Sy0$>_Y0`3w;H*vM1N%Yga&HB!?znFF#COAu?kUA z&<9dj83(#P2k4ntLUpf)!sQ`gs0%b#!R~usL8Y2Hrg4&Jp3B&_qR9BKDERpj`X4O< ztNb2l6ff;!AtR@j)&xso^faw!sn!yz)r5DOZhWWj89EX;nnM8l2wpO8iTiHA}0L4QD}G!+@EL0CaO5=mB*tfIVG4A zk$-6n)6&ILozOvtq1i=ux9v^R2>_=xg2X_0%7z*(nUq0843bUK%tZPpP*ZllFj+eUFGlZr{)$skKZg<;WXy4Fe* zDBC#N22yYjr$RO1I++eHi=YnG6ISTl$42+Br(9E-=r5^RTYfZ~J;bf$aN8N%^4EHy v274QA`Bro^uPx&s!2paR9Hu?S37bOK@ja$gh5mxCs-l@ zVLQO`?*yz*f;fSF14}1BoB(kWi3?!kQiLcMktIiRAj*z*pnE<$pI&JTm}qO~_xkng zH!}!c+wOESrSP17DS*uHTl=j50L@JO5i~>?E5I)`$j3h>lYg=8bR*lHgB~b#PCE

I60Q`MJ-pCQ@!`pGJTlkg~anE&#^(dT)QE1!t#) z0`fPcO$w$ehJS9fQLIzULjus=+b@`QswfG+;+{#`%)BrFy$3Ns82^7WMd23rh2`tb zG7#pr2z55th5Mr?8eqggV>pM#a29mT20L(nRCfV2KFwkCGmVjTl``P!8DP2IhlZy@;98&c13-QvkD%Q3{501|aK9K~V*?1eKYq1Od=u!v`%y;V;~xUYynyWCIvK^7@*B!p|K7wmb^op z0v`ZxPk%-tRe>ZC3xEz6LTU3I1Mqd=`b>HP5yn|2O~fK(stiy`gw4|_+#lDIG_?{=!h|~Vv4eLHg#1?I*9$?uG?gAe{IG7f|3)q zPN$CE*!Lx?4$p$L4rK-mibc0&oKVLT`#wcFAM7|=-uR{urQkFi-m-o33ijU0Dliw= z_IW(u^7`OKhM>*u>&oJSnFgmZxSS0HvCj{a zT;XUX<_h_hQbs@tn1+(xN^7W2_;0(@=~Vqu%ZjI22ro!cg|xWqxHr~4&Fs?dak?&j z@k65mfMm(|acT!}I-r)JQg4-ltnw?_EU-$v^gL(bq3Zk3^WV5i4$$&^Y5@QM002ov JPDHLkV1jD$#pD10 diff --git a/nym-wallet/src-tauri/icons/64x64.png b/nym-wallet/src-tauri/icons/64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..d5c694a23aef5596b0b8007979c004dcb9d78235 GIT binary patch literal 1984 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 z z8IK3QHYXxyh*V^KP7#MNyLn&yK&+T>;;3?xd9#CG7vJ?C^{xN%D6y@+i7X0qs@suSNQIh@xM6%IEg{f z>uJbhNHUTM5o^xY7FA^tExG2gcP(V_mn&qEzw;O1o*l_vjXfFfKi{w%nQ3!zWYLCZ zAC@UW*_-rP>qR|P@_a%Qzx=|t-f*k({a4`31_Y}&2kBOwY|yYD#RhYaF&jjM%T>DH z@*{=8t{|wwaT&F@ouqH=K1a0E^jU9niLv9RWq22}JXjqOIi$^0$JrkA5ncE8BVK0T zCnY1zSZny6)k1-^R1IG0!LIYEn@x$nK8edv7Ysk(p*Y*2!5Ni`vkALdx%A=ac7^(C zGkx`@tu^Qt7_=ivE$`D$NoQ4mbn0Q=s_(5TpQV-LX`ta_Ga5f#I4wo^^c9ww9M5AkEht+et|LCvkPoR?eU#K1ZI~#9oEGG)LNYA=a=8@bqqpev- zik*67<>6l4;N^w`!xBfgVJ(T!!R*RK{CCQsV7NRHB>Y5AD!2Aut|s7K$0wLdTB5YQ!TtX2MY@Xf%#__ZyH z=B{H&PVDKAvjJ1vyRf2Dk|4!AB?a`1irle(Ddp0kB+n-Rz!C(2A5~7_PlvT)h_;>n zdBtAZ?4OWg_yC!7kT}LjZL-PVRdgF!Y$13VNtTYtBvDu28*-X0l3+`T`|qd)Gz4?~ZfWHDOK{9IEi5t+PS8#j9YVH3CO5 zErVgo(2|HB4pL#}8sam+@DGrFNoZ$%o>sLNF3?z{FH8Ao}^pJSEDJJXqruu*D|$ zf|BZ^1n{nU`r5;Oq_Mr*!<69uLAZ3wC%AylQ(VAX&09GG`F#(+716D&E%K()aco$wX*u$% zX*qtbYDYX0%xE0IlWH<^n@OT(uC!tjpz%p8;9stxxqo(+uGmaHPD*+h>e-yuvw)I5 zKgd^WIX0-71ci2zX$|5s{mU)}@&I(4tC#ncpVm|psM zgIgSX-F&vy8ZLC3sba8VZMEtR&5J}au>ZLBL#Y|%ACy+5e#A;%#VTq3y-Ae3Bef$S z50-z0*{r^GO`3dmf$|HbKeiAzM{8#Y1p~H;JqIsoyb{$}bJoqBDO_D)KUAG!J$MZXdDg{BFK_iY^xJsNOk`~t~g&g!CBcFk-1eyQKGrRd6(>N;djvMI}U zQ{M$u7%Mz^y9yQ_8bj6;E;$8YUjxA7cS>KjD=kIQJ;=rpgA4ghb>=8 z{bnVqmkCp^#O~?gmDEHe+(H((37m~cRd*g`O_H~Cta49}`$u4CyU3WjQ*pk)+nWHO zL+!zG%AREvgdrd!6@1x>#ZBaMIx({P2y4cZnRBHXHv|$<(xOH)?fzF3p`~qBm)lJ+ zg!-H}($emJel?6p+`{Ou@^tj6E-`lwV|MdbgXrx0?>P8{aoAR>VBGX=0QmjBzpRpm z^U`KTUM^shJOXy2I?>1hEfB#DUfC+LtS8wOnI45#lXl^k#iD8|av(#!(TpOzmEA&X z3q|vYU$lqA6?ra@J>@1IJngd`GwjVn&Q@bRYjmKBC~=#x4pUx8^wu(T*#fwMar@#% za8>Y=?V!gFgD^9*X67}Dv>^`$P~TX?dB#xhR|KjI9Xd1*pmSk|QHu?l+aa{|(wtPtys0}WJb4Cy>avs={ zhr>x~HZ1D<=MgNbnVJk2t@F;i@Fly8YwFD*Ebn!w*?5fGj8vFEev{~IQ>Id;A;$Ni zQYTqOpKw-N@Q07-dLVI8MC#ckU+!Un!`N2<5SsY<-;-l(R_7Gx5wHeHpN_n*nNDf! zY_E_@6En*y5eLgci82LOu^)Mx+XDoDG$D_+s9S_Mg3jR*9hB;Bb3to646<4=|8kpC z`FuuyaWFB#WoYayUQSEbR8H@+7AP+_$~C#QO!@O2Ayq5R2e`IuDsSX%t)apOJ%X^g zdy!JWcCpsPuM|W%wy@Rb^&2Nw^kr$A$)yUMiERj)?Ic5wzq}*$>~@=1RXsTkz@Y~a z^r>{8&TOmK2ESk?#G)Qiwaalm`k_a;faO!})mPRkBUN#_6H{O^d;t zrP8edp9N~&A=z1moMds1QN^~h4>*dONGW4A=vT@{A%Ay7kzblN{)D|&S>jh7qIkjTA=x5Em>bTH4^Y~3+1IVxQW!#3ug)V(#X+q#c(NFb zc@v^*yQ*jyiseg{9F&!(nO{x*co0sL^%V6>+Pip=n-g9*;;r%=9SGE(+fxIj9&+;b z2a`d3FyeBX){CEDRcjT&*4sL9%xaLF16|!A>`wIG)!vYED=m z;kYrHHm=hoE#!4W_?rHg^a+cGBeF6tP@|H{3*|z^Bmn^(@DoB>kh`uFdQzf~E;5_w zSrBYJ`u*Y7R;PZ!3i|;Rn5j$-Ja0)7an{sWO8lIIoL5Ow+D~sNLR>FL&``0BMnS4l zCB8M};UVi4r0eo>d_qK_n;szzeENl&(#UmRj`xf>;Q0FXq0qP^u(1Y$?;3V=VmT~- zaZ_@qa>tH`N9C5pKjJ}YuFmM>z@5wg5ut{wOSn2X=%AEA!zGP~;WKf;P0X=A)qKt; z>BV;s4?(%D{$;s^=sz!=uK~c+;LEb8ZK#c#3<&^*WTBX2ShHi;#Tk|(tC+kqZ_Ks~ zj6dl^(>9#TQ=UT#U}=woN`>jZ&-W6!Oyz{W(*7ekGPx8 zuo8k}@#s8Bf@z5#6-jDN9I`TJGQ`%~B~@#Um&4uUA8LcQNj`nx!b8=3_J`aru}jem zlioImK5VzBh!*v&J-{B~`tK67@XCGTt0r6JzwV-W&L_Hvv{&ww5foa(8z^^^>>Z(zqIwhyKW*v-&5Id*?c>V9&bOmq*EN%> zefx@9_H`UGHVEaV?!3BN&+ppdGs4C?R=gsW@dC0WK z?VyX~=gtYg-+QEL>sM4Q;EhL5StT*ureejcBGXKU9=ht(@_n>pZm60?85OF^R)WQlWZ_gQvLDk zwAAVU+B)Bl@VU%BNzTI5N`KBlOW$A13ToCk@3_QjI^NYj)tR?zJgN};=0)$=R%XYSddXe8= zsdc?O?K@r&sIXC^fy8Z!7s27^HAO(YPg1b<9yf z5RTM#xVTS*c1(@G2I+ODJPZK4Tyl6oeVP0bGZh(vmK|J{IKj;Kp<{dRq+973sq6+O z{j@uZaT@o91e{1pt*m5L8PPvQYOoDwY$abFDZ0TY#`E=ljZ)9R@ETMW1In!^1+(`i zVc^K0p`2eRoyI1XI(&LcuGiuQ_OHFbPj9KFCpTZ3^}gp5AJF#BBY0qP&GBop=|vE( zUW(Bq-_-`GUA@xKN-ocjoYR`maJzH<;*aTW+p#``6G&Av4RXZ)oFUcSL$lcZ*B_5?wsWM-mTVGWPiweCutPXT- z{YIZKBOQ*@>~+AEeVM%K(8sg0^Mc{Ap`+3$WSIn-7?)cVVU^U&qKe9{pV0syn@Yn? zrok7ZY&Q5V*Ax^Y+8P?!alMd+X`%&|C!S z+t3LAJBC~D2OWpjq6>shv^HLC=Yl_#J)gBDJv$~yD_2V)!SDF-;M+Z0kVAjcH{J)zPp9eg47Lo`*p8>eC7x^E9Cba6BrJdSBQ)m#(Mz}ZGn;cB5 z&#dOEss1?*kL`kF9rv)87Q7A+($o(9K;f;n3CilDe~PS>NvrmoB`2RMXJuGxJEMp@@{efDEAR@Te@@wd6amYf_Iu ze+itA5Uj3fD)f!By_t4GDOvW2#?3HiAK!ktoWto1e@k`Fl$(sEm1$y+8^?QPchl&! zb8QyEI_4j)+SlWXJqBM56Acv@loZM}ElqabLr;IdKixztjI$`F2P`kJdG@ z@yWK&tH1NbCpGvGsr^%>f2Y0K(jG*T3z!`K7$8o)*XiLm=6K;( zs}@*9TbrlclM-jBPQ4%l_AyutGGw;r3&)oKFgT!nz>HRH2a9o1A7s+X+Ey;YPQnwo zUC-6peRPmJ@=foRgNtty4XR_NO)2^2<@gD;!4w?EKXdb9j3CjCKEZ!E4zbp2wzkG_ zH_!ySMC{?w<^(D#<|517zC$J1t3+cICuuYIs~JIDM8Ml*6v?G!SoD69K+}j_)C>!1 zv?-XfhN4IqcqZ|$=xl|k)e>9X;PG?{M4|&z1Ct}(m8M0;XRnJeGpq1LvZF-{8RnLJ z7H)h$+a+cHSVPIK#_9XhZ(SAiu~>DV*fBE0!fBpzjF6tQ5%^l=Bw@ymntu z&ZnR-owgC6Abh1$gEJuaY9>PqGrWgBHg5y?E@=H0qljLn6Dc(pL3wK{wu7~87~<54 z7pG~jW#vfi+eMiC7+bPrqwFMt9pRdoa1$&lbyi_;}1qYVd!O_1U>I92&9GV?p=RyC3Oy?M#h%Y*1Co z(sO2Y|9RpC*qK21oK=m0D2bgLJ37|RNQ>%%?!&?{eV{}4qd9$5NIl^3^CU&=wwBRB zEe{ofm=4{VJz#xIGG60*fC&A?y{Y{G{3S$?ITMyom4_`|q_FJAzEP_3cKT*mLeeG@ zL3(5N<=svFOntiFD_8I1vU#nv-g;HF^*@utab~R}1dQ5|Yia&#-pLScxBu~g?O2Ls z(&*hWFyaBbsHMzLy`c%r%hZegfv64tI4QRXX5v>m!1x9Z4}AKTX|om_XKlYJbi}$4 z4@uhk$n%`BW?4u=Kutk7b&rg&RSO=o=5*En?#pxIdfxPI_}&_h#F5o$cv_&~QH zrln9#znK(Uk_e7?HbLykcHXX1**!stZ((b&L#|<$0{3-!;dV02Dp2vJpsMiKb^@>U zbTp<=EkJ9n$v1OAv-$)#&>A~$eK|4oe#O#~IW!h8uFRLzp$5z!7tva(wN4hXOGZDM zJ~8A7>E(0|cj?4*&W1xlp4CCQQY+gDM@GpG!%LM_T3LBWo!|BIae%~S2Gp4+(K%>L z6=kz?=UrW>a%qMskzb{m#Mfp2^2DBfY?>u`bF55N9-_2Br(IP8DZ58Hcvlzhq+QEA?awJMUAvF^D_!bu@vhuO($pMLtK95&#|t&AB!*H z!|G;)`TbV#VidEpl2+VLUelo-Qk#OEE0yzc&y>zhey8sLga=T*$!}H7GeZgOVc=#r zM}bD4rr~Ov;=vkbK^Vkqw=Z^J8Xv^DFzZ<l2><1ueI$Ti-bpzoFa(XRTq>xnME*dr z?v^P0H953r<=X4UJC#R{B{2eg%*+0tVg4ZALYTA zMC3q1O)$jioOoXRPPM__8Ekz3;#ME=kN5=fD`uzYZgn zgJa2NX@11^+)P)yKD2jK&IGy{~O#~(<)b-`!R?A$woITF+=SfDjq!ggC+ga;zLT<<-HdnVNpC(g}P zNs3bSLrLbwW61vN-j!QJ_X^9Q1?;@1)!{_BKGDr%z3rd+w*$5-Ip@8n1+zdqUWMw% zHOJo71kdd3C{o!>`GB>o)emweuL6g80pmB&tBq3W6hb zzw`dnj(L9_K=bh{IPu5)q+25(j3eV3)|~Bj;j_nw%pfs*-!Av%0_~Kwi{L%$F$zS) zO3<7T({F1gmxuFD1GW6^G+Q_$ve{`kQvkTELh7K_uwDmqbm1%AP90yTCulBC>JV%lFhDgy zg$O0Yn!)y6_KQ)<0wCHBs69_a2h>crWX4dmnVn6ck<%&s*s5>kfUS}Hdm<~JWKCgM zGwK@r-wzLqe)=d{JY+P@ru?LqO|&ETZwvg;0$i+W2g8S^zJ--!It5QO96)Nl$OwK+ zv}oM5eh*%1U2-I~ebVEJYcX`vf;z0o;zQ*UbJf9`s4!Rx8#of19k>%fB@TuK? z^}lrI`*ia=0v*a5vQXBvbxp^`nfdgS|7^mGO!1*BO(Zd@lY5WELw5Qw;lp|B%J1pa zU&76*(k+4jM$XRb^02bmWM`S?=^1+SYHp)K-h#sxwx8ioazQo+&PayiPLsCdlv`a6 z*d#zHGbu}ZaRHC;w?OH9z5jtaQyRQwb8fF5>57*pH)-jU$;QerlABt5=Y^{2tG=R8 zL*aBp*s*m9xHez8R2H_HwK)aa*OwVC@|R3!9SGm^2Snt%Dss#Fb6h65RT}Nh#2M(Z z_TDt*B%XaXNzgNI88xc*YQqMjt#KWwx)@lSOH-fDUtXR#JjHCogNCrnO662t5c%^iRzCWl{wXl})ax zfsVPE4#C8*85eLO<$G9@KlFR4(`o9^6M6TQLc|uF#9|f%PadDDV?VFzcH_4#zIC+; zEW*yCSY?cbPYPa^+(69Wv7dWGf8TWaQBVnaKB1x}aP115(rm*6nDTUuMWW^Q*N*HU z;W!58b?_LJMkMXsW#kZCaZ3B{2EFAo_RHy5Pl?1HThpNLx<)M%hr|GXixz-R&RC~7th#T4%{j`VpZeZz0WaO0C3`xCu)}~hs^g7 z?P6x^ljHOJxx47s0T*rzVaH&V3mA{h=je5GrbH-;%`O3PBYbZK`h=@iz~pbex&00* z4#%PRIf8v60912~$}o?K)}%>Y_b-hd9gJcy{PdpQ*(7IAZX2@DHF?*jeR1k1(E=74 z)u@I^$~X1iNgrXK?hKKukbJJ1^Qihe=Sga$sObyXRl{cz|6LZ8_impCdmVHTy=F~I zM)0DYTPlu0Twi|2!#^59T;R?hy^fVeDVuG~qBoZveyv*PG0_Y~PvYs$gbJ52eFCbc zTBS+dE3fAMB=eTO{&B9_g}nw_iEwsCbfSfH+KyJ1rx-Pz@(=b2RP}?>4b3D0zU^y$7f%Zb7CiS4Nyns2Hwf;a88q)RKQ~kxL{s}6Y zNtexXWBU*7!Bc3e%*QN)iIJC(^S0${P`BD8g+qC`fNB(jgN6d4X*KJmPso;!$mTbv z4-)wi^@|chn(TF+aKX7diu$`<{*D{)u0x$2qVB#E49&dVqEaWJL_ZXwrI;R2rx{;$ z17fMDC@c-7quM)@hAQ{Q&^tyTe{#*F!=_1HJ z>GJt@9|its=nHQte@7O9-YYnlg`6K3-*ClcHuQcm)vz*BHF66l?x{=7q|Hhn!9|R&0G){O!xOUek;!CH=SGxmxvhce!?>Yt&sz znw`RMRmru9p9malt(|BIQl>=H!ZE%ZJ(?upu4_Sbp3J(J9O% z6V0HkrFO|9NZvn>vJ}q06_kdU z;^3G1>dnDWSMncv%ePNCA#WzHUvVOs#>&!)}!7UZDH$gH=u@_WTsQj=zV zC^)ne?Y`H`0$*Vw7Tsb2^%`Fe*re<^o*fp0@h=nSy3?;8H{I~l2h_2igPs8W)`hWi zB~jWve5HGA4;Ds6T7HsCz&%AjA?nqA-~2qdH;@3byNM@ih41uo{W$?Wjxo-FpaN($ z?`H&TT4)%NezkmAw+@tt81Ub6Nt}$lqp;PwRJ<*^vBXRA2?|T;z z5cyuR{L2O8`@+kItKa$x*&X;_bpoY$uK%+r?IPX-8*Q>Y;{R8P?O50O`Tu;`l^0_0 z_g=2Nce#OOw@wLQxqsk_@=v>dx=)C+EBdcdPq1j$QRwu--mB{nw2?q<)SV6Jv(8IM z<{Q7JR@ebG{SN^q49<^iYO2{aAQrwMF=gV9|eSp zo`k-dM*C#qe9mRpO=*jqcyby|SLqPo&sgcDarH@(>1DBlm>M#>ndbteiED0r*w(tv z?tqfE-LDlHy4a9p;khXvLE*DH$tOhPB%t05$olE~)2kmt|IIyG!ZFroFy}rZ!*Cuf z*(v@aH8$OAH~MEpgKm6qn>B_vi3G%%U4!&lweLkdiv5q=d8y_*N|_V9>MtV63msjb z^Gs}8f89B}gpCaZJqvpJV8OY!XW_c|a8-2o-&WZpRxZ4z%%DrLX5qtZ&0D8+!$`9~ z@tYOCo1c*?f11^wh<1E+xGEQs_dpjiaNI!XUBV}BnDVj}vkt_m*0G$jg|9(LjRz=J zq-^~9t$LfN7qA>bdZ{ZUJ-C}B-^uBI=H!30#OX)2GQobM z_NDmB*8Bt6rP~wx#(hO*uMP!lrqFK2_ZZ>%zTL(vnRndTGDy-)i^e}5raEtrC>S8$ z4VA7>u6qs@lOz4tZ#gA2_zLNUO~aqrhg^@V#z=f>!!{p!ke#AXB;)JSj_I2HWi>&#h9^TVZjm z>ePs%QIonD(>v!sc)Xz6i?e!=*%4`swmH_@G$k1XHO=b@tp-KsFdjTQzp6!uG9USN zhveGzrt99n*U0N2JDqP237^t5_6|Te0m2iZ|k#B7Xz5N1>qk zaa*b6SC+GmGlu4dE?JXNiCq8S(-AWn>R(+FgVRdj%`C|`pEXlnx3ShMM;68)DgDNe ze3=)c>xuvka(H&^_}4M2Y5uLr)wWT^4OS2PhKQ8?a>E515owF!R@v#6gemPNUOvoNW)*wKT6XfkBxUeX5bCS{+KAKIPHLiEsMh*T$=)qpxQ67$$c! z#S%1~$^h>v*5=4CA{_#mcmP2 z5+C}Gzf*RNb!6M!Lx5`(grsq{tyzjXt%Q(4Y{)SFcUVZ1fn(Ta$T0HvdC3T$0NbN$ zHb-YF&(BmypR!Kd98%+XJ5m7plaw^Wq%xEvR7}>(CRZC!){f5ml98f zrzVsQdIT0VS%NHY)dc%pYNC=4twd+0?lk-RBbft$uN*ft03Du~t%0}OEHxl_1MIpN z%?n2hIR0V!9X0$MhmQruvl7aSAG96+_KCZUyer#TQi8S2VdTEQ*D*sB<-J;2|5$h^ zruL4Abc_Qs`@~m80Y8FFf?MJFK#TNoUO)TYDZ}f+LQh*?T;Ii=S*70ww=XPQd8K7E zn-ZLTZ$v-4-@N`nj&VRkth$=Q%io%Ty2eWp%=QPCwg-<3IsbFGSyC-c2r)ll$Q5w= zh$7cw1Mi=OzK-4MMXAlmiYiUPF1xxSMdMnHv9hW{ql%D8QPl%{z1-uWkaN0Tayi3b zZxhR!6oV#8$M_?hJxU#2gxARRe;4ixQ}T?$@u`2Z%Ggna%eKZ`8{aY|IKP^G&l zeLoy&&E#OMr%S6B4bFf&C(-(1bzSO>tEE@=zp1nYB_=!dwp)b+P#!8N?h`fD>WFRp z!=(+U-68XyX}HPOHM#3PX112^=YgH>C5@}t)s0Vr4#H7|5vL+;g~B#BmOY?pbAjEw=0)K%V&j(*&A=#Yp|TW)wT8>(9p_0+WRbB za(Q4^$Lb##T>7+isnS$VEOGOajd9rf{^#D{APZKM3^+|~+lTupx6hs?o%BXb7Z1BP zQ<@~jcA#iRoa8?LAFSvFU196`o1UQsOMKYb#oxK#c+GbyU(O>t4@aFMiVG51eqS~o z9QT-wK*Yp1dEtv;_4*59-;GL_&kZ_JO3?bqNb!H4h11>9xyK`&nDMGr-_<#z%eiUK zM=e|~B&%tWfW0yd2=_N{8Ad(kaq`BD|4$j;TdM^ z{20E3k+>r4|H&GeJEpe;P?Qg23%NxM?hB^yVY`4N=Tjls?eoFeB_ z;Oa`T8vNAGwTHoXlD8gyEfWG1Y{Y@qEV-xTlfVZ{GTbw#efYJ3f4@jn=>JY?Kr?ee zktb~KWd%QnzFI~B2eFR9vY8Y+IylQXTWwY~lhwcGndp;IR_q~%Cpq-X9e%cEKe&S7E6 zTqXC~!SHuXYovZN*?xL`|IO~jw&!6jKh{`xLkHO*yjHS?%l4_deyX`8G5W8fY-P>9 zjeOroLyUC8uB_|BGC06w>qu{>ew@a;Z?AvP$?dN`j_ytDRq1@YS-x+7UlDcJX*Gx0 z66Ew^105LmTqPF>J-#O?ZT7|;t6PX881?rm*|*a}7xmclxnGcsy9@Yw<&~DQv>7zZ zuQc`ndCPqwL`3sYGC2ev@cYbo7{L1IRx}2hpVWLJuDohcD%CGAl=hf1VkpRA)M#)^`m44iwa+Y*a&j##c~qVMnRqQ30Y?Obb`U0YHR9JVlYY9_A^Hrv-LalCCNnPOV(pRw6n zAdUAZUhhOjqn&0}Pe!QM&}6kq*u6eMY-@O5mEM}md~oGv+VA|lOy4K>#SR*~4}42- zawH^+Ofb{73Oq1FR*MFAITh&+`TO*e&(bd_Q7!Az)cC$y2zVGC2jZr$L*}`ywzI@& zB2E6Wz?Lgp+TT^|)Jq+el=w^tkm#Q^WF4gxZAx&k3$b3O&$l=mG8$ZB*-A?yk>U5? z$+nF81CL{j2oXb{zmKWI2??QZ$R)=W|M_|#nANHF;cAo3?6iZ-M!K~uQ(?BPdq4l5#3$fKBx_>V99>&+!2q5p64Jgex| z5lr!1$u>2yV%lSA)}pZf$fMq14ilZE(O&bsy@;T++COW1Y5;a|74Lg*Rg@EoUC? zhfZ^xg*xx|&NK53?LX?=f`ms6>a;s88Y+Zv3TlX*Y^Dc?o%@WL=f4i)Trw&QTLkBS zOk^jePYyR76D&sGjkUko;J*+-QpmKb0oQtY%fvGkI`J@W?zWO$@d|J=_pgO;sn*-E zh0L4ET!Q<4cRzWc{r|9Z-GNlT@B2CD7|Bd!i;5^BTQZJBwxsNp>@8$u9Q8@VDjC_Q ztdzZ1oFZgrbYze0ofXG%e$VOq`^R6-`##V6-p@U*>$>k3C(@`3>%D18=9CDSH>KTa zDOzq*(pk(fe&KiNDsent@`#qF>xO;p-}RKkw3fng(~0$G;h|2?j1G+%XZQU>PMD^@ zJO~QTDHD9t*7;n!zWJICp6k|4kidZ9IBd5exu4yY)zCG0zl-d}LccKN45R!?_&dW! zj}L|quliQqaUlr_O}Sr=@X)i{{G8NW^-=s%fh6UCSg7e%5iOL2g&w0Q{@W@SPZ60z z>FC^OJ2t479cGsv07KDf@BBQH{z|6ck@BfWqY0y7lB??itSx<;T(H~n6U3{+jh|KS zEN`%F%pD9y=E%ALwBPpK`+Q?Q%ngQGfBnx*JyH7;)*!hwx}&I$_2X8{xww>-(Zu1^ zrHPajLYMuOV#D^_b0e&$Wj)bknMjWhVOc9PytGQ`$z11(QB7Fzma)8*d(5=|b31)M zwf}b%uoi;FK(SdA>3S8RDB?^(Kr|t~d|j%qVM0A9Hz8=UmU7^F%BLNEa8RL=Df#Us z>$0PNh+KaZ%^kV%Qk~enSoKxDvE9dnI(otgwjviUcnO@-$fY!PV;dsX z!`D=B9hQ~5oPE2#^hFV&7N?vpZZ~WC=O#Q|I}DY5?O7-1amY$wU$)yT^Ek{kKD<8` zg}Q5Jo4vNOT-b1W)39%(LO8@pFFNBJv*25{*GuzTg_68xG;+JK+z`8}ggRW;V`bbi zVV54eNN@#O{7sw-HPpOczPo%fDb%4TNc&lnmW|9(QBar31ZMfs3eJTSzv;rI^~}QY zYDCUnPrh)&)x}bRH_M~gW(At*HK!IWBe7S{USnp~pK7&P-7bSYY=dO)Mu>8L3$r&A z7(SuEE$h_yvu%1N#}=txppUR1ZnJ|~+QOaJs6+-}ORS&*KFLoNi+s5zyG&Uo#O&~m zM|DpJLLXh)bz;t>r(o5OH2$8vKi&NV`gZFrdW`~5Jl0*C#X<3vVc*yjHxSiX4 zc-KD5MW_5B2~UX9qK252n%twKhhd`L!#WLu8S?pjLBZ!DJap|gr|x`pvpTRDGT@G3 z*D8i0f%XDYi0xPSa(_TnqmUVb=q}}s7bA{J|BCki;L~%}*w^#>c$-l41;fmownxi1 zw#mh!m@4C%>~+u}T`{1ya~|1Iur_Bp$7wmtDDdDQEorE=V5nbkO_}NBjo4i}!F~#z zq|`6dB=K;;>2Itj&s_qTPpVW<^=WkbfRK`NL&mT#EYYW5tA3Gx{p#zFoM{){HNpf{ z_^4#DrEafw4L_F* zJdt*MV>Rh7v`bQl`C)FskfDH`ZOK_*JD|b{h*AQyL++BoRFxbMcBwSd2wl?x<@*^q zyJqK|dNkz!Fvnc_PN*Cq8u=VHo)JDOn)~alqJ*dZR(>MK9x)`m82)A>M25k8Q@MW! z`S#(=m5v^r5$bPk&*5B-8CcO3d-DUQVIAa(|sV?tL7BWy*SA3@XFuREQl zIlRZ|q~zGF;QCEC3p%RX7~9CKoQN~GC^0hEP50+`d#V40jmn9qX?EOuxxwIpTl~@ZWzTvMt0~ z(PGtasl6X?Y+c)S(@W@RohA0Dqn02#!2m5F^}7$dmcM-XSHAeKIm6g0Pmb+EPqTc) z9l108>i%`i0g|tmMD_T&1HT&zP;DzI1=ryI5||{LmgUzWTnT|u4EL2t)W98w^S@FG z>Cmu?k&eRx6e;dl#SmmsXtmEETz4nX(#MaZL&j$t-((hDawISNo!aoLS=4`Z70oHS zrMvCMS%sSDG9Z!;A=_eoaso#`L-nA^A2$_#da>$xEBoo%8KtEVT|<+p@XB8B z${=B=grMrTW<;uOcXCVl`3}9|njIx;@(`}5l*Os}YK`88CX3IguDKZSa6OF3z zG?$v}%TT&Z*(MW|bew5@z$Pa1Z2*nLJ4?W1o0a)15t zTJODWVzGQ+kQCKSDH|NMabdiGum0|f;NF)WW@2uBjkm8$@Of=)LD_^F*~iM5kKR2G zJf*shy4x=w)P{}?WC(aQ8n^wPPzB5}ob zB86UF?;_-{wHA?X1YXWm^Qtu+h$F?e7x54VND#Z3=*-@?%4lC95!636%ASzvvBO{G z3fNTgunkGoMAc?FEj(*jr^FJGw~d}eXJ0<*VcE&%K_{$4{w6Adjfeb$^b=7X+9s~T zxl=2rJi3znXJQli{q?Pxhgk6YWt5Xo$uH1Q%jM|P+H_Bk0K26 zj@%35=VQ-2Dv43S`?ybM5j`)>JzA{SpXn`sP1)z1rGJ?7!;;SOu{27)7hvq~4t%AS6+;6*M%vU~>>e!qH884Zq^~&h} zrbv@n8+txKUfMGaiAbsRe&ls2{OG0x@Zqy=kG=HTvq_`=ZoC&Juf$^R>G;iDaXJN* z^y0*!UHA-+VU~sSfuZa#Bj?MEGDez1ZnoqtwQkcs>_Q&9&2l(QoLKs*l-=j0*p-@9 z`)Q|2&Rg-fh`MLv>8&fzxo*Bz@f%k!7M3SaI__VS4NJUslc>YG%i-#E?w5(iBQmtN z*sGPiOV2gpE|)o8>9E5HA!=TD3*$7}r_&d9>pH~}#||Y+Y+@5-D`H)Q{^aw}$l`B@ z4LKYyXHHp5i96v_t3DXLZ7f3nf$kRz2gO^H7s-^x>zW#9nb_B=N|0iYWQM*gK09>o zo;XfW5SvHJRzfSGEM)eiPNG?l*4`M7I=Fr3=(T-(J7Qe#q@dG6_LDIVTSUI<%Vo~r z!wGeJdwM3?Z8qE0hK8l~_M;#8#y9vmdGR?yvu!mL588?0>j>AML$r@guDM-aX2uAq zz||Mau@+Sntk!nATp|^3M4rp>dAH7**%J3HJ2)Y$wu&jV==7H4!@WWGZ55~}`#Z~K zMV)wXbR@6sn$-Eqy}FRjdX8@|;ECO)5?5$pCZwI&;3)-04#vU^+{BK@q{AM~H%hCl z*e;S@?9j~;ZTez?%%^$d&!SRh(@#oK-0Kb$8gf6CX%9v}YVu;^kd8l9KM|2?>>!m9 z0BI!5eXp&my0CsswF-*td^w-Sm<{+*yw8X3Uq&69K6A4~Kn@df7+jl&%p67vpRc&; z@ICvB4Jr77;U9X@%>q*xc>2;WFNgN$6q=(?B7Edf^N`w!Lc=wBK5Yo{O7?wHvyS4m zv$a08SA6*k#=W(s2aejIEZ(YrY#^bi+8rm3@%+v2$b!4kFY#L#g&mFGf-kAX8Zudv z2lN%c$9=ly4wwNq<$|J{7CYL+r~QK~rhIEKEut))K1T>8>&eU*#6D=O8@7AA7fqMM zN{%FuHd!fW<<~AN4B8YkT{SIoxsLv zlLE}g3%nNL#2+3NCNw8vDSy9TcuwQgmf-VXW>wwWG9TAgLROAMiGPI@THKbBk-7!V zBk}*$#W$N-+CG@7eiIBchZZa~e9R9@Udu~cE%Hrdbc$0Idr*DIkU8r+12S%uFg|{p ztN&myH)n4Ksnr_zIldIs@4r|u+6ff7o>XJp-D(0R(`tjX4TQQm#Wb3;_+m%*X}A(? zfl&EM?vQMBMX6D2RL~34eSL)Iw>G%rd{GPAB}q)Ketk45D8~*AV{~s^8vkZ^y=cq# zx6MMR6}!eW-Rai}2>uF;#Ex|p~U5ouLY?O>}rb72=&>Qxc zB~oI4fq4l;SW(dXO^I4D|I3t>^w5fN2NoW~TNPO*wqAOVs~;(kHr*0O)}8^iCl!(@ zFtcpuTxwi41izon1D%=y^ zU4MCojar`HES$N+3xki>4tWA!$kb<{@bT7gKH7p7l`J&2dS3|rsX7;--@dO#l_Q-^HyLy`gY*1iLgI$u<<-}^sD176Hq_fuG1r2hwgpxmi3;lHNJ4zkik zRj)<{MpiUfb%>YGPvmn_e}LRsFsMJBxx-N-G$xoE7LI{ez6>8BW=A|Lbwm+`W{r0f z=_2JvBZ4?o%THJ@Do(@{_B(;hqHp5c7mxIx0E=RN9akqLo5wyg-9on>%gGE;_#VJA z6p8`s&ym-9%(zk5i*qKOPRuYv3WgaH{p{XYYT$_AQ&ITy{2^sG5uJ^xg8I5#>N?Df4+p2c06|Mfl&!+i>mTc%X4;Z`PH}N-p+mQPyoxL4sCtt5psc7>%qNqr!$T4^ne)c;H-Q;*ROrx} zhJx`-4(83k*edhbjcAh`modJ9P6ihXv63U1rA5 z&8RM9aqK4%t;{$O7lN>o_k(*R)2bjp_a4I3zGKf0wC0%|hW>mRz*Or5a;y$rcrkRC z*)xw8S$9pH^;vdvY!SazL>JXP?k3UQ62Edg<6`d~Eib$R?f`axM-_i3ZfN{&bl>N1 z^W<$$}a(eS2#@73y#fn!c=U zG3z!9Vxbm|_##hRgQQpGqT%PFAtR&_fBK}i)6WlK9NF`3?DJv9U*%@F8G!WvPO3NY znqeFN4(zHkJQ*x4(sdtoi%AEMt3~%OE}JZrg4#Fd z@=ENcGTeP;fE22!#jxkq^Qzbi;mpQ$yPj66*_jn=M@HIa|6^}@@aZP3L99RfrTOZt z>nM4TKUerZ-F*RZmVG+1VOpvi5qcTb8w))6&dAaD3DNjDYj99_=A;Z@yTHzjEE_K7 z)WbHvldWjf1Lar}OCG)=RVmVf*xNiu5)W?2}J!A(3`|rUZ%w9oBpb zkZ^zKvmPwcv_v8=c9EtpTe)X5q6ZTZktpE042yn4c*;Isxy@$mVPrBaZi-GQ{F6+1 zMKaMt3NU?x)DbD-sj=I!53~kp`c^4xV+EZBJnhE{7sNbL6bbJ;K-(-^;*4wQ@1%JD zel7Vwv5fVQ|nSmZqC@Zetp}2=t8x z2<-*GPpzrD@W zJ?8ErCDUGT;ybbJ<7~Q6A7e~QjvSm{=z_Ip@P6}KK5aCm&uu(B zgc(PCIwXGlN&*(Q;cI^k8Oa02wL{Oe*Ys~;>EUcgv9j3c`v0ngG^*-%6zV{p}j z>!G#4rPC103mt=x34>3e=(VDuKJkcIiwUOy$4Sux^J-B;rS~P$u+$taBvgJM(^vmrhY56d~p`+mNwKOt_qs|_4uvtFw;AfhXK&-Fp zPCdR+U}x*9A3fJII=X0u4d;bJ zYHQVhOKPotfwrQ;Zp~>CNe;naQh&j@#}_f2`=0dieGPB&*#VS2 z(6l9o-P5l6izg`JZ^G4)Zo=F5QH^$_OE}`W105l!~V?G;^CW2bKl?I zSejOf(pA3?;g=UxK3vv!9|Qi==YKP2!ITi-c4kVCK~SS}gTe4&Qg$TR&Y;c8l_A7g&mUvxg?M%zo8QNAR)hwz<=zOsN ztpT_}&Rnc3`ciwiyf#RA2Jd0*y-nNmhn9OQ7XciaMM|rwhpBnvVuo3Jt`~H+FHN%Mm-}z>bl1Af+@61@MC-r~?8H^v_cE2Y5IWwGm~1n?l2-kdGGj$e!7@^}0M#vIF^9u>ma^X;dqD4Nj2uJp znHH)B>#=Y9F#m4f|3nf(fNS?pUt67_%*sg{31Pxt6e=M?dGY*JukC)IzmwU@7{npt{GbWN)O99V=$%U|L35D?GVX?rUDDQnLs< zwSS+xpivR;e+Oz#5`1!FvrNG72Ymv`A&r99jsu)Wgmn-X5V(|c@ODGyGx{bR6pm#> zx%#8QX;e(Ax&$>4omv(DLuGWA=kx3Gb-wZKY&tXw@Z&wrL+Bw8PR=tt!rh*W-pt60 z?+^H6L50TCVkHkf43elO*Q;!J%ClL~zb*^SAPKo5c?*d$D@R%rp=U3MUG$y5XT$Hz zG>Owl{uOfPeqzAA(s7St6upTE|5f~F?#RrjnR*}+1J`9e6aN5++1Rjr3I=B=^3MF% z$5fp4VYx1Cb@@lh?L`8-q)f+*Za~ZXe;;VR2$!Z6-uv+<>RqHY{y{SIe;3I9FU5rm^h$bLO!T~wO{2gX zfb<)$zrA`eE_!tFg2mC*>Y=Uo;;{^A%@6?{sc2SEkjb$HZ&i5&d8r__7f()bhs=rZ z4LA{VwPuqe%;4Dc+T~_Nx^uak^>mXqkjtB&{c%6Y(ET3Y`m#D&bh9!Vwau@7p2&u7 z@}h)u_^@7etQB~5|^TzzH`PAnod?m?E{8UP}C+m-if2=6o*aah#UZmqqn+5qF4um{>~O zAaoA+F!H;<_A{fYJLD%XMi-@SS3gUS|~_Q(KoW6Z`}gb1@_0 z47Z+uUD=0S^FH@JDg!EnaF73{(r8vs%aGsLG|Bz4MhD`8q4uc~HGgT!i`2XhY zuyBnY>V{J?V)3|39-nq{jpn~6js=UwR&<-+?4C78fGgd&3#yT#x`6}77lSy6X zkzYPcE}w)i^l-M@;ZN_oB-cojy!Gk;=E#&~tk4^O#j~ExPUfKRfQl5Wjh|AQeULiS z5bs|X>zRA$KEm5h>w7qiUo7bToma<;H$^#SY(9AfHnotNpokR%muCHFf#Xvoa>d}# z_g>!EoZbU=nh!FUX$2!7n=hJ#j6@l)U0t||bPOc*&W74|Oj`*c;s7QA5vQ~uL0MU( z3{Ar2#lP!pyY$HW?DtpztSv&GE}I7fwMjI=04qMf{+}>9F_58s+A5~31(~DUcUg2Z zK@=Jm6|R~NbD!)kc>$oSX0o`%AW%5qc;Z)A+s$QZY21eJz|XzXDOCMZ3hF~ zV%js&xq6HCR{`%H`-HJ5e29vN`K;{HO@wZNt-@NL1E8z=qaaP>ij2r;A+e>K1I#ix zuPL?&-!Wp}!aE>Rb7B#b>y#yo1TV~i0|x~g%lrJE1iwQq=SF*wwT2&8{|Q@F=)vFH z%&#u34)#vyc{JgX026-}US^14pORaUKsjr|j+^0Kb6Pn-()pcXNrN@fr~KQ`kg@on~kEU z1--yStY~m&2JMG9s!(Fsu7w2pGNs^Wup7LeI_RC5jSQT4=ktUNpKAVdq5>8-oC6!z zS>EsR!8S#1YDbpMnyWk2kJWg))stmk$fW%@5`SZra&mP)i>e(i!T74kBWTHX#y(4H zjney{BHs#6r>lDHnQiWwy`C9b*JhML;)z(lcPBrbNHX~jB`UDiue$U->N0I8Jy7S8 zI`KP*Y>{xDYkVe;qI8d%VKd!*l8(0AQF5=5*Pc-3qxEBNjH8$#>0eYsy_3rqOhS^T}Xq_(EQg?LHuw&;{Q&I zt(wJ=6u`a#XDGcGO-o{~CsJM+)tjKNIIpmrI?EY|5cJSoYNSF;cNLznzB)G}hJn^> zS1!(s(=2(9oBKZU$_Gx*dgu+Jen@Wd8Fqj(mDB4LHarwBhM()U{ zx6Dl{b;=jOdaUKP4zOt?yOpf-jY0<>f8#QZXY#ufb!R~GXJuaNDA#SFFFV|qEM-8x z7zClgh!z1hiiv%6=?z%ZM8H4@Yg?E`UR=sHv2L6UEp+GhSP?$ft8;HbQ>3){#cQAb zL^@3QJfBhKpQx#!lVqOCfCO;xqWGCj$<=mr7s$<8i}TXc6l{@gWHiDzELHji3pp zg3X+e`;6A&c0I%4<0Ze@$3rWQZ&b(c%qkJzDvfRWL4+p6tOc3eFR6JIe`i~9J#~Al zgPo@_m7nn>V%y{+J!=IcE=}{RS$r0!Wvq;ik10Au^8%X1z&({Zq!$4?$O|m~eDFd2 zJJ<*o#k^`(9Q4ANBAIST0w%(${gS_t-GhiFJ=!zW)Gt=S+&bw9DU92D@A*y zBG9(9aOQJwdMjid)S%|TuH7a^Wf65N5OB@AGvlsAM-OZbva|e2w$o4Pqvk#HSDc>L zB|O~z*cB=!ve!VtiY3B3T43Xnrbpwu^idzDc@BzR`)vUYs3C$)Ur;c=b#FG$1WBwr zpShTzqW5JZ;w;iS@^`y>y0a)SN=Ck{%4W@^a@3T-S=d_MQ535^fkgsyW^$>#I^p$` z8CWPRvY{=^m-sLa%K6fWAQreatkb;nTdLwu5xsXgY7Y}5>4cmnv<41H!2)?DV_q@D z;AfOY@5#89mNFR{yZIQXB$5idM^kORhG{2D>=6{N#p$t}2C!3Urs&%&9j(7YGb|cB z;?=9C_#yi{n%6Fid$9718Pu9W$j1j7^GdZ>$mLI;-MYboy9Gl<&fAdyW{7`2VruV7 zkNdgBfBG}5un7s>I}TS@{7*VKEhmS)u0?BU$JWO~iHU#RMP-c70^gv;D>W^dZcRk@ z0hRgARAV^)X_@_t%%0b~jQM8^*^6ubotV;R&%0AZ`@sRg>>>qeLu4+Ya1Y|?Fp6Ce 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 z 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

E`;+TI0D)U3dqDJ})h1@!6Dml^X?6 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 L4xIguJN>@_inh_@ literal 410598 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

1o$pjC|H*j#uWRoIFX->v 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 diff --git a/nym-wallet/src-tauri/icons/icon.ico b/nym-wallet/src-tauri/icons/icon.ico index c312b68a412750670e417ef10108fbb3af9f94b7..9b72bd77ece5cb1e3a908e9fc9728a055652ae29 100644 GIT binary patch literal 14325 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 literal 67646 zcmeHQZLA#ES>BtZsqH3h+{R9AUw_=Ydq4K>&hFiN-NsHKKxm^z{ZK;eI&oqRHO*SXhP&&-)K=e*~Ap7%Z9Gp*Jf{rmK%TlD|2*6Oag*8WzjwMstT2CZa{a?A#}<03LOvooo`XtCr90dqrP~>ia!{1P7IRv3+p7?=K3d? z%-{k10o4g_jE3ze0dE9T&snmzwss5o`{$2utgyO>=X*UKr{?pW_-3{dv=cInmYxG< zIe-;T2={qvd%V6mTH${FnltW|e5<*v&N4*MYgk_J4ORR@(6bC1UABn%W*|H-!wks6^&-kN=D=$w=jV6l ze!$W=L|;Ov?^7{7p2yBn@J}<&-{aICpfRH_=lB~=hQp;N(1x5HeBJ$B6EbgCTgdta z?Z*w!w??DRKI%hULqCFa>E6ztn(vz}Zs_Kl28|kxn417pnL3-c9+}pU0$Zr~`mGuuqcCY1I7_*(UKOVfFhY`x-Gfx#H>3Ex%fIr0|!q z@&o2%^Jgf%&oKOJ%uAG+;nPrFV!k`)MH?R5{O6K>`>W&ezRU+i#wMOU7UeJC8Cm8+ z+S+=`)j#OtHKH4h{JtG>GLVw8u?R!*UZ`75cEF{m`u}->j`*PYnw3PN&X?&dW$mJ8y zO)?`M|Bd7Ih4`$@Jg;{xvpnnNfT7GrUCFw2O~)RudvGZ|@v%ec*44T6{P*eiUH{+3 zAI+tw|9n^4J<>^GuR(pyvHigpE$^j$tfPhW{lES$N_Thq=yz^R&tE3F@N<<dag?rrH{xWG79F9&2Si`QOu%Q<84v9|QO2bR*44|EHBc z_Fwe-Z^h3L{`2(gnZLd{!ymqs%jI(w&|JF*+f6w!Gxs`7HxRsv% zi?^n0%>IjfE|*=w4`2_wf5%Gt!Qa}OJ^?QNa4ZL&}YBtHyaueV{z2Z(YIwyf^@PvU_qf5hj<=im>1!}lCZ z*A~Qe7)8B_^1GfFIIpk?T{@x9P14izE%X0=_!s;z>CF{?T{)KDxt!kjJGZeq_=3qs zOZO&xfdBgNGq+N|ualmA<)%sfp}<&{U%W;gh_p^nI{>=#q zdM~K{_rPCOHuSyM4tLUr|Nbq^{`Iym_-P4K_BggTbWN%#np#pdF1v_Z6ewJeaht=x75z_)1Pxb8cbs2&-(wGyi~HT z>|3V(-acl(=l@lWf8O3G`Ts0F`iQ5$xr?>gx`#R*_{+SSV?Gf6|7276d+3Vkuf!Mk z`bSm&P8EM@UjzU9|KM$`&ehwdnio7ieE+?_@lMu%gg-%FbnC}5w&pZ=T2A&~0Rxo{ z7{hALE5pB;_McqNUMCwbJok?`WbJ;bUgwyYjvTK$e{Mcq-A`k<)R)Nccf(swo2PeT zrtH7II$@gjkG$`^f8ML_DdUB4y>tIOmnP&(fc?b&uP6N7eY|XAlWR4HRx$oxas20! z*ZAM0ESTT<0QqhB@4Sxp^Z}@=ANcwnX8V{Yd~@Kh#&Ux7M8~!;X8+W0f*#@fWczZt zcJ2R*vh-IM)73kc(o-){Uya&tF8P$=P{lBKI zUBZCu75JaZ$N$~9iLtfe=ooQsj*Z)?(zS)5&xd~g@p})a-NRX1qAC2h1GX;w(&Df5 z!|FD5i2bj=dnvu^!hx*cN8>j(th4*Y{4rm7_S-jRYd@A~9fynkTX;R2?LT*a?t-}( z$Bchb-#UyY_|sYqm+_yqcDh3AE$;ihcVzf0xn}3YCG!0rdG^+9u7>(aPWW5$+QHwh zcbNwh{KxA*3S%1O`T%3Tm|uP9Z{CuvJF#FQ=f|$cb^qN@-oe^_;B+1M>-~JWT%ui7 zd)I8Jz3XMtmBVcRZd@?qUoH#!X&W@hjye9vXueK~FLA=uC%8b*VXkoWzN4()hq?MX z@GoLoE=#@l^?1h0Hzxl1`mcihmvy#6<1|0@)PCmsFKEVmIVW@+zh+$|~UZ;rF?5&b^2A697Yn9ps^jQwNG)_2@i z6JO`IO_=)Ji)sJpdjSJe{%kEbwbdVa{l;vL-qIdd@q*9yqW^$?1kW$SpXUGA_dRe{WmoJgZ13+`Ox8X>kXP~!#W+Q?WgniEZ&$ae7<+M zu~1l}6V?7d<}?3q=De))!gUlrgy-*M{~8w%h=1;JbgKw&-cRi zu{VJ0Gb;WwHUCp?L-8`(j}!f<;@a^qw~bYXKAeg`F^zFM7_J7 zi}StMyQjq8W&M|e$B@B@Ki9kA$n`49HLY{2EW{sg`3vaI{!m?Va)e~zkQoi z-LK<=INyssBe`xVeJ}rC6aKk8;7A4gETlteS+O*5lBcCg!-&5|M=~}p{vX(&?SJO4 zZVvX%SRW|P_wsV_qZj@P9>=hME=#?~qTj2MSB&)^BL2GdzZml!ouoY;*vn@a^Od1F<&`-el5@U z%K1zme|6bqIcMp*5&Tj2Q(x^Pzkh3`F<*JUSJvb7@z>eq?DC=s{ChOsi}yL4eU3DELB?H>z>h-_wV{*3oAA3Box0{XmhSdGk z4^hqc%H;{ie^S(1Afd~Nu1|I3dmKA7-lV|2s;?9nid`Kr?8VY>NVSqHO) zzkq!M^?}}RnB@Te*z0It|3C4OoD2M@#hO{yc=OMSB z%J<=u%{o^$SJq>Hh4z5u z`#&c>U6$8$9n1gg+uww<#U~faZt8~XIiKJQ~F`x&$wRr9?C zJyF>k_y09xBbU#39Vkm3_;WeY=f`_=(079mH`$%;9=Au;d@s+3A4~WPeqEQo9FG1e z>O$rGuf@JN@3}nN;mGl~*Z+F_bp9Xy`hAz)osId`FyA}TZ&u$H@h|%k8Ee%w=UWK= z6L~7u<$<n^bdxGz_%=gMV;dw?`|GN#=|HY_ps^OpETEd^!c%dDR z_c{T4{d=7Zyr6kS+kEdd@QV68_Zxb$PE7_wnlE9Gfc=_xIZL zG*$ao)U7sotB(_e`UC*j9FZu)9d@nBpKdSNf%YS>; z|KU6U{%+%cMQn2XFg_!#|HdAlKF#?)TH(D;yzUqG_i~(dM>YO>x$`mzyjJ30vh8qv z>tyHQP1i4e_OG@j8GpvQfh-&@cbl`$s%c$aMVVBGgV z%krA8op@5fHOwc4;4hEW7w{+Fk8z&||8#fwd!5KOG3INV@0Ime#-H{WYV2IcMQ;Df z@t+3mQZAe7+MgrkxrY$^W!>fY(;8pAhY8nCWB553n3 z?`IPA>Uz%g?=s&j>&t|{D*p*5CfwusE8BlQzPq4%&JTX9<}YH_0UZBI>p$onoLJ*= zq}5A*@)yf{d~!W2?Y_tp)Bau=Z~5Ae{{-ip50XqA|GN7Bvdqfs0(Yu$MVY?9W8jZ( zov=OGp<^}FUVHRAH>7u+JD8sOvt8!-K2v{;uz(y7{L!B5eZJ!UUM92Ie(d-cxt4Q4 zkcr_>GE}yILC0ncbY;?o$K?aFfBM!@mGvK(!-d{ZZ)1f&9(o6_+m52rj)?>|nqf0FuL*ee|J?M>j1cTmdfKl1j6QO3+qXzgFnnUDVKh$~|% zUwh?AYc;SxZ{zNxtPWuB;)neQ`hXl$S=Xxc#$kAeJFvg!*KSXj>HEK4^5?iH-v8}X zj$-|gCx-PRA@x1C9a%pEN%F4@2?=i;MLuC%QVBnzx z0mo9gg?qV9H9E`htDXrCtFNq8 z&9Ys0JP*}#<>$76ji|V!3ahM(u)3`=o2pX23i`6x+Ldil{vh3%ZNs7$_qJk?Y$mRn z<@UqNnAR0)dtV-?;XG39(YcnLu{lD+A49kH0YP8{VF{W8P6 z-~K9veQtf=j=z}3&bh)bHO)}4bJn5Vs z4BGptF1T831NiASj30H_j-T8r9KFio;di~#)!MXBz6yDh&8G}=!oEd+!$Iez(V#r{ zsM61N3oGhf(4U~=Vw^g7Ia_J26V_D!zYOg0N9Rv%t{1;;wcT(w%Ije(#&iVE%tkyl zpKY=M>%n006B+ug7V3a?>H`$(fZ5`#txQebhp*Vq{sI`I?%&+(9^m-nxSq7Xvf=6j zu3LL@;vX|-sv<)#He2teg%5h2< zMUlNGuKQqDu^iWcDgCaEMxA{s{O87jTm+`W;nEW- z?B%dwzw_neqtJEnZfz(p4tSPXZ&%wJL$%IqWooCJgykUVJTK#{yFNKTznl69uQkyY z&|_7FM>d^QNjv1(>B+hM;D4+foi;=tu5IH2NnJ-i6PZu>s7F`~X6VF)Z$ z7~8@&`<*t86*j~H9~<$BtHEz(+7)B@BFmH6{nBM%Xv5ibf9wZdM;$V@Q@4IW~FWob>L{z4xQ z7!mHz12X}_D5s-g`$@{<4K{y3V@WfKuV-IDS%wm(B|Dpx-6w7g*%WE8aXpOd68`l2 zcoeWTiS+yJ!<5IDMnl@e?KEC&(4C?zN<5t7xpPuwpQ^BG^3+>)jyrIGe;y8(4{2bF z9^C6K52;W2MXF~~_yablT*)UmU>D2QVx{BEOB)Xy%0A8um_yf0=Vb4Lr2PW<`xDUt zc1&>8T{xN~olnx3(YHvptKh?keyEEeD_dtAO;0vG^84=koT`0WcnIA=htMVTiXU`L z*WaMyr<$~P7dmWqj~*EG+7I=63!f$VUcmZd>@Of4PxhEt${Zh$ZNi1JjbwW>us)yG pXp>%^ra9WP-EMK--UXMPI6E5H(ZG%db~Lb~fgKI(Xh77!{{u9YUm^ei diff --git a/nym-wallet/src-tauri/icons/icon.png b/nym-wallet/src-tauri/icons/icon.png index d964ffd4da3fe235a5cc25140b4fb9dcc8b1d4f3..1c7a21d21e41d18f27bc349c67c75776ca89abc1 100644 GIT binary patch literal 22435 zcmb@tWmr^S^fr8EhLSGnMgc)W`Uk=c(p@SY(v8wRgVG2{3n-w{jdYI!0@B^x-3>F( z`24@TAD;Ka8y`3`*Tp$!@4fb3_qx};)Q{g#WQqsA^x0Yopck?XwqhgwfJI)K+0d%pU;z zclqD*1xWvWL;Sk{|NafI{JQ|4f0zHKxBh>99`>8@e~e&zOzP>Wn!MUoBl+2DVqNrO%%PPMqm+ z!rf+3QBCW52!F#q(7`&&{0(Spqj8{$01xn4B5LF3jBhY4_ZC)CP&C$;mm!r?ret)V z;K&o^Q(M8N55ryGt*n_yD-z_uTVpOgK$%UrF5XPOcQy-WT+*PXF}ybDsaic_O+f4U zwrVF|Cp=Foe8>0pRZY_RbrHJQy1`U~LoWsE$D$ZVTW?hi4Q3t9+oyH?zu)ezeA>z@ zhpr57>5m*T$F*On%|&P7e>l>1JolF&ms_?xxs5M$dKhvcmvpA^=F#-y%^movWJ7PQ zY3TaFoD@SGTu++A2(4pP_~djY=5IO?k31WFyLc!?!4fulI<(uI-k8>n2h`S2ZWA($S&A1M20j>nd`dV zkbW$#;`u0tYS-Xq;@Rej@a?^DR&!&cxNraGnB5JVd*B5I7<7lq>K<-nWk3V2r0$1< zU70@KEK+qHHz0AOA`dRVbP$F{I-!G4GnxpT0z2?_*gG#iJ_4RcfEPuAk5YDm4e5lp zKd3NSmvTQ{pA^7v&$J6Q{_bz^DtT477n)jDes{K#tJ=l^M<@6?w*2n_8I2t^vcylj z;Un}HZ5l6b^VgElax{wUQc5phG0v8IBy;utjOBLeq$;&FJ0x=$2blKAX8w*hl&*bw ziQ8Sp8z203_rKH+2>95eDQ}r5f;QugZGzR9^F^{}N2(|5yK-2hz)&fdr8pFdzFI;C zL#Z!s7Ke68T6yA0+cr+*_f_rM)I8f$+Zg^bF{=ne&t_X4T_1a-7S%T0bC}CbA6nZB9ft)hG_ai zT_^yV93$TM*Uw7vSt%S^@sODx_|Y5E@!%0J*FDVe!f`J7d9Uf5z5#0SvB;57-UW4q zw;d-A7O*E@Z>Di)OTqxX-dAqBtQ1#9C3qb47k=Vj8{*eS&8fg!pSQ=?qzpF7+vCJ) z4qZebatH{vFWza>JZ_DLrF?p-Zl>Brk3O6^8G#xZ8Qz$jJp}_7;mg0{*$4ZWpI?cE z|F~{rQmE{ys^&UjOXd!$+#<3xJX!i0{Z;CH3yp%sL<24{O>&LdC*DX{qP*;sC0g0sIUxm6-?_9me?8SSGimfX_tz^oy?^-LXdco#5gU3J_eK1kms8{GO_pE# zvRVA!`PCgX>$jjlaS=UgQRmwf3!#P4Sz@-`2)ihxq?uTgNU6Y9C$+U@%s-Q zr@Q#!xw&a4_tVD)(Pix0n7LK;{-7tsAz_s8-GF9FzFoS-`7sFIdgB%;*Om3 zHCcL~`swVGmNOtnpw=FET)=}B6?vDgygM8zz8$D8Nb@D(BA9>;Ye7Iv7mNT9xcJwX zesN!Xw)W!sn8mN^+Qp^$L^Cr+W;k`d+D(`IuP&RAahnWEg?;)0EJN{&C$n;m5YaP- zh{f*P4%apQh?=XKkYj9r_f^QvN za^C;ai*QYi$A^pTXs!2eX!~w0*2y&jNDjAwL*je3 z!3pb9e4HQ4{;%-r1$cc4k!D8`+kwtU--zt$IU1(}V z3;xvI>|OdDo<$)MDYc-o9+kbTfcT{9jTx_$N_m7g@-zYy=;p51;5*;ErBJf%4Gm~D z6wO~ql4C16SQLI!((@QX(k(qV9Q3!~)Dy(n0o{VgP`2`Q1`oRZA_Ly`Zt#!ZWWP9kuE4sP4w2>-EVJY#+d%xas`e1nHB2iM=$=c<2d%PEV*40ZqpLmEzU^oK! z9GQDO#vXS^^yKOmq#P|jv%_f?b@vDgMA7&xk1nue^S&Qp=Xw&%7U^$m6^2Y)t=U^l zvTm&IC&2*7sP_%jXRV{Ik6GeuF|vK}nRbh%FWrik#hWjhfVmgPgXxMQ$4sXYRi+1) zl1bB`ERjB981Q-9`O<%9swWZpxKtpnccYDUxnj9x{wF5w+X>gQbB#{(s)aoy5(~Q|*vH zx>R@+YdiArNbOTcYL}y9)~1FGpf>7G_Er4!h%+3Q_)bp;*v*-4OZeUvqS}7eX3u3% zWiu%87pbP#rKEespu{_-?*JlJu~{1XyE0Qwt6TE64l}w1T*7VTj8h?;_|8PTp)2>M zrrL9u`61fGZuGmX?`8+yB$yC6X&;l<8~bW$0}n^1P@WhtU(z~!80OAAz)?t5FQ;~~t2FnyLIjz~r65IGsGFSwy3rvyihopFUQ*7725B;bvqv+~ z+Ze}wv+aFPoEG&^sU@1S?e9jpkkA{wpYxLG%=ste=7V9lJqb^s2J9Na@sv1UMCDPq zf<*Uhnyg@?N97KB=y;V`U)*C2o?_V6+IN|zl%^WjpOp>>^T=0ZbPT}=OQ?~W-)Y%dsPewNy~GxAb!O|iqd`%ksEw*QiaTd+hSI7hAV zrvr5op~7Oo(bci0Ik6m8wmWUvs_nqt`10@HBt6dbj#p5;zy;=qsys}kICI!Xq8h;? zTq0y`Nr~EDo>%^CIF4RPjg=uKL%7IgCt9I;P;bg|K0?Y*6Ff-_jqxNMezs8TY1wGqdI9Qn?@4oxaWe#^`oxhGD z$mL-f%jf;re%YI`@B0+cX6JX7@c1HH*(sx4L)~d4xc;x^tFOMFj$n*JzK5$ zrGlXRxpDGlTcY(en-~KbqLvcfsa$@^v~56?1NU5P*B#Jmz|M&&Ncs{hDfbY;P~xEl zOCSt08)fEg4$mM44bf3iQMLY6I8F@8%ypN!WO5OOn^$f6Iv#%gvFq?Ek{@9gT)|un z(9qdnP}A`34Koo5!*#3xVWwAZ8}pBuemA&#Ufl#}u7wX#6uhnJoA@I?zPiaf+vBxz z;|#Xj_;2xpz|p(q_QQ4Q7r>rmOK7NWPxfV`0G_|Rsa=zbhie(!OD{{fE6tK5RxmbE z61hMFQD)87hoo5Z34u{}c0s5g#t*TOt>t;{AAU`9DKn+8JuKAajhcAc9f6pK4(f}- z5mLgq39lw{l}Dj zvq5qm3;3xRmM^;6`C(=@`Hnj+3;uNH@!}&gbnD!EFQ`8n1mv|n9+KO6z!F}Tujr2J zAq`WDAOKbH_whdsP&&~z<7bBldZ?7ZWs7`3^_umWHeHfz4yp*2ry&CP)PDD<$sNJI z`Cwp*+UgXpKnN%#+%rd?!|n3}<}I~x52(vFrE6txnlnmjC>Sf!ANBZeY;KacH?aa2 zKkoUXthO{wjtjNw z6ja>Hu%i@o;Crr|$(*kiXcNEssIbcmBPxN$Z za%O@D9lcz+jJ&~q>=z%jDWy?ER=LJV!=xs0W7?}Bn5v)kb~W}vr`KhV()e&@^M=R!SAD&o_jQ1Y*@&H zqhIHqMg)i>^Q1@Y_q?ifeFHLa{){t)>>Ur@4ulP-eX#%gZ>IY^R2>pHGzdHXp^M?L z$NveCgJ`rMn2r7bq9ng67bgNmK}bRSzGhGVUP2TN5G|$w)2bk%t_)egW7z$-;X%!| zkpzPyB}O4!a@GZq4H)4J1Pq~wP#Q%p8w`*we+GE4B`BN`|5MdMYLTBX!kzhP z7N{loj|?!xi8EAV^kw#zsVs5!q*HS@nB0>rBMZ>+aZWH1zK^Ftj2l&f^ZNxC`~0 zu_3ECB6Uvz;p8vbf3FK#qw`IG`i;hdg?KKd3T@h^C-i6=`|rCAk$gEEz9J&W#T1Fn z@pL?s>hcL~8p>L(v24dkA0% zZY-aHr~lBQeNkVK0P5e_QJ$aooMBQsBf(r0BXcOQgB$U1?5%%0rjfkUT#yo6E&tD) zw=`&VS(pU2^@Rvf;MMed=dLidBN zpSKbMcvAvxWxjL#sGtH{hle8YS$q%u;*u2Es;-xPbu*3Zye59$yQ;{4R{ywB^Lvu> z{GM+mcdqQ2Q&+wz7z`{0eXsasX#e8Bg)d44r7pn(5BT?1jUD8JE5`qZY%sD`F5pBsms z_$-!}zMYK%WxR)xVZUwy%TZb#W(ld1OBvi>#(r*q*Vt{K&%bX=)!Dx&q~LmtRSN#S zP?-3gApW}z=ji3q`#dgT@BIUovB_e;S5e|lTRSdPlg-o;93T)Nq&QWw?dC^NPECHo zoW6c2?JO$&ZY)VLvGKQm%UOOP+@eLB{jF=$_)9)Nm)r6f8aFY5o)Rh*;&g8HN)`YxyTH9alr$g| zSu6i>P$={qvr7Ww)?Hue?l(C;~6bs%|EO#c<} zY#&PMvaGr$Vk?M!l$UT5*;VB#m3K`x8}rvkiLHJRgbjWIbn(PZ8pP&I&D$&*^>JwM8_7 zPbBdGyUw%E+Sh!ljtW8L zwp*M_YJMJfmEvBcAgeW;kPijz@?uID7rwopPx{R?FJ8@`=rJUFwUrwPT#u>~WKi8< z@4H>Pk0xx8$+F}hxc3$ocK9eXJ)LOVt`NhIdbG1rJ2M=Cg~4<4w)cN7j|ROx2~!+P zv{X0!M|BHt%+Ob?x`jW?IOH84dJmRK?>DwN`dJ%)zT;7_Irbm;eAdFw9C_?crIzYs z-$Mdn0hHoA2d)+7=c>acDNfUS^ai43P1cP-i)$t%O}3oP>HcIXf#b~vXw~qRaW@aC z(|G_BmbCvBNGh%m!;*_pTsebK|H&chyz3y3(i;-Vs|BaaOh3Jz=!K+opkZNm@oo1< zXfw-z-^uF{2f9GY;5~MX=E+M{<~P#EM1DIHp4g~Vx@9LfDH3>TVkG`!CUzpS)QvAT znBJo9OO~=4OI8du2oz5v3Wou}-Wr{yqvT7^vSN>t!vJUb<;I!SLpuKzqea9nj551q z6NkpWORddq;3HK0kT(=!PwT&>6auw--nS=s zj0u!~f(t}>-54XSoLUs7(WR7T8*xN^e|(}7_Y0Siq30!h9xgXf2g=v zGf`DKNjJ$Jxu1d?Uq^F;r|4j8NTj?bK?88^{WusGNQWGJpcTJlEcTt& z=`W_Bw*~93c7DTjzRx#wkqFlmMW=FX3~9E!Q9y4rdQTdmqjCFp;65X3hjcVm#^a$e zGPo`gMP-&l>4Y(0!XWr~M|rft5Lb~Id^|V>W#89h*@k&3OTbLRWFDWqUnBG1oEL?b zTY>iNAA?RCX;D2zg)7}5D|uqqN1#OLv!Z*#D3?7Y6oQ`>&UsE)cqK?l-Wg%05m*{V z;Cwsj*ZTpH4#*M#L+e$z9i9XxT)G!YcXno?{Zk==7Gi#HMn6X~+*26&)9sDv3#aI*EsV4Sx^2@@oV2eb7in~TajKYoKg<20l= z+UdAGxGh7t;g)#rYdmfAzDgA~t2GED=gGyZ5FbypL!4_96WcZULi+s{-v1`T0$`KG z;$;UmM=1?6#|@DHnK!6XkSWQWafhN0r2wYvZAY%jy&`@qYxi(ROcjzp_&}*9)0J4PEzY3GxbGY z*T}oWs;MxvN&w>d;P7gw%-4qP0nHE%G^|1)WtP=Z0pnLdyX z;a6iqZ3?voUL zaHy{{)qn;nx?9Ry=5+W7@c?t8DRhf2J77pbI`Ip!<#50bLeX99$0T|dKh_?q=Kbr0 zP?4Y$JQmc-uvvTnqg-nBwy4X9W5~kA_+TlU`GjzY;|o-LHH5>0oHECOq}h2Q*!jAs zlwaEfv>s@OkIV;^oQ5KJxouefbl!ro0FNr!uCzKF>3K>IkMTp9evdKIg3C@Kil>|B zSF05bpUXNUjb@v}H>%AtHTu@JztJ%C>@GgtrBs}>5fvaO1whEkz}K48{_uohkfH0e~n9O}DqG~CbIv?9-@13FzX8Rj9b*7#RIM(^O6UOrC_geBDlp~D6Q z&wv+K%~`}ge1AxWIYXkhI(LUwNeh{jpo9KR-_`OaKjVtXF;Xs}cm=>?%tc=j)VPdq zhe4M}T-ZfKJvei>kf(y*rCJR)hnAESKt4mL3`kCcjoOwSpPAg9Ovz!+stK54Huwed z&qIOoQ6Na*)7?=yqmlUp5eAD%p3O-a?rGkgz@ZFV*YEB-O^0HLgbNr!=N*T{3VA*S zoV7@8UQ?B>mVri$xDC`7*R{stnm$!?lNNdH2x`xkBxuD?ei0-W{KQIJE2wcZ8W^YS zZW9wNo7fsfm>ZF8~WXV8tm8<4?g77?6w0P(kq8?=ukC=_buw}VvZ*9u4}FZ zL7y>j3|kXRSqQMlmDs=n}P0!tgMD z4iwHxHj+rV;!$3aH7!5f#;JewA3#gAH8DXz(T0vIC$ z^_Dmj^5=MrP^PQij0BudTu7~1P`V2+d!ZB-cDJ4{V;5BwsM~{JF=J6;Ws!^BuN%^roV(&PZncS+&8dH(>tFFQZ-r-;n@Lnp? zcXuvGim~Eyz4C$ei)^zjS%0C9g^Rl3da#5&*5JX++tp!Mg^aQmFiF9=*BF_2TBpgHO<;h?Z}8VjWM0vUFkme7r|iU?Dw&K;p?a_x$uofR++tl4z| z>~Wh-HE{{YEb6E|Rui!L&9jYpXxVjDuvnmC1x5^{R%JCJXR2l({5FY@UN0iDcdct06 zKKRI&-|vBR_G+w!T-uMT{Wo@uj8mdqf<0m+%FO`@|Nf;;i1E9Dla&JYVk$?pRoOv=MzBEBISt zs2p}OVBc_T0($(cD3&8n3&euLkvQw7$Nr{XNP!8w0B*jdB8dAx5ycUlDS!De$abV* z_^yXFyUwrgtmsFSiZEr!7xlZzlF#6pBGOM3LKrJgLv1!}DH|p};<~VbbO>plZc1aD zixo+T3}I{Db7(E6v%=Q5GY@fwR$z=&W9gHF8zcyT#mW|sw^k)`?9GV^Hp1y{*s-v( z*SEz|p7DxhuJ-cVFTfts*GYlArC;NHqW$YBCZ+>l292&?ds-$G!TWQIX|*0Pg2cKY zVl5XYN03h_^or8nET|arjUOxEX!3xx2F8U80-k&52H3aH7>XG_RXgkzWPGb$v~%-D zh#FZlM_e%gnTo6y;OjOu^N2Qng(W7>r8p$6sokg$sl zis>0u3s+)(Zf<^@q92t{G|qS5DB5?n03}3tK(-I)z$fDMX>-t$W*;3Wg3m_G7*wY* zRa#ENn{kW`r$}ry%7gGJnY7y&P~+pIz{oLvhX;NO_Su!5S-V@`?@Se^>&0yyF=x0}%SkBW^)BEcVLlfBFLY{s z1x6L+0VQsvKi11w33ANc9%XfHuV@iZ7L@2@SzLec)FP#cvTKo2yOLOU95Xieqj*67 z^(->&&UlfMbLGbWVJPAsOFRnDL36kfxhRnHGe%X|eNje&H|1pk`fjq$Pj=5Gf^oA=XBds{9!(i88QwCsgGW_%=p}Fu;(fTnnDNnlf>b>A#Njc00z(`-12(ud6quL>4CTb|(j9J6T))fcR&XT( z(TR~g9Sf6CBc6G zb(6E}3K!;7=wN-$+P|d>V<|0tuMHqZxxj!qwIg9ncIj4bn{8$8e{kRaR?;IMowlqIvQf^i)}}ZzYivBkwASL>`e?KGZ38gi zXS8&*lfwIU2&EZV{JPTqzL>bHm9gUaBW>VV?8rH$L>MU;fzgOJ%fjn|svXjQcmm0H z5&V&?L!C3E-8hUlTPBHRpTiP8kd6IA2pG8kw zK;4#^zHll?`?mCqLuQ9;=%#j~8GWXIx8ygVwZkfZ7#e%wY$RUm4GH9u6rJ1lAjWR^q@)AG@YR?7wG5 zIM1LuNMN?KYn;2nh{Zb*e58*#i(Ui(AZBO5x7&zztqri11wr$hk}1e-OclFkk(j8H zAsRmtuoFX|{`*uNMzH0T+xP$j`)_aObw_~~B$f{FxmobpHyZHxKBAVs3{l2JoRgH_ zL3Q=65?iBVL)=}+9ltu~2!pG@bJocRCoijKm_cFw?f2A>2u?)f*5egiQWYsgIhas_ z5s8mgzt|Y_dD#C%%zwQ%!vz&en%}87ymwS*gjK9J z%m(+4SKl|tzDvZxvmTJ~{4x5izlo0x(-Vw50lAiA?-9fIV!oE`j?)La1zE5$66TY;)F}!WRH8EgE&SBFc>&`^nWelyQj5hT3nQq& zJI#PQ^Q!0N9dCh7{7R>a4(&DtwbPLcb`T+Wbfx zK|s-i+YNu@aCcz@4_xEK(B(r}T_AJ}?8A&Wv8ii+y63*Ha@ydhg~ z&el(9Yv&R~=6BsoE+tQ2%c96j3t}Ie0x#RMy|A9HZ~DEF z7kfK}eiBUjyup^wuur6KgXeGiPlIk}ifsBSDS-`=OLf*4ET5_od7!*$Dxcf+ne!Cb z3rxJr*22l#!zv6#NIcLwK^K;$&l8hfI>~oz zk#rgV)8^)EP8G3WLe#K(pgNE*h*xAew;E+?ap+d<6pyFnA@Her=&qy>+s8v|5=1$- zgo!Qnfir3CWKQlAqoZygoK_10O7mw<+(7`pQ~Hs^C^5~9OU!>MV!BK*_dZK1YOy6_A_WGY z#0cFmZi)p|moGA21fF`llcHuZeyUbdi^Ob1>qeI8rXh;<@2=|?f_V%L&i6b2^5J*? z08{fMo&Su8lr0rT2U2rlsC|b$_5zta@@UXyOdHrPU8HN7Js7Vb<5(R@`hmz zd(kv4ycO>ALs@0Abh8@1vqGB>F;73OffW5itk`j?D+kkj;Qc;9f#WgCHINfQrU`K+ z2w=MqA*}8AVVNh7_@N8|0XdJB_ajTPC`$5R%N7ey-_ILhJ8R#9UWT(Jiq#@Td3Ny> zhJvLM)M%#_u>jKA4^nL+e=!-@+I?c`V=xQ*d)HXp+OPU+Cet5rGo#fm&bnv8j;z_t zfed?^tSssD&KLtCynY1T{Jbs}sR%8VKge!kuBmpN}w5@!^LxhtbzTGN)|Mf>c z;Xj!3KArG(TZwygx9G1u0$7UR;yLIcYqRV>*i=oT;DwNGYXPlq_CTQ7G~*vg6+Z;y z&7^>5Su-705FhMty0X&I`W|bR7^~L#lVse@GYDypI&1dV_u>NIG45C~ zN`NcPR^_Yd6i8KD2r2Nxz_!6(AM;4SsRr`rg7K=&IO#Z7Rq7NaDssL&F&G_A1pos+m^(2=?p&?4x;L;mM~VaSW>hT$n8;KeTF$CN6T6NAVsd_rZr<`f&spBdn5Y zbP98bStL2_0gN>Hw*;7d=5LAd6a=eun;x3Bc%q@@rZR><0;qp^3-Zy;=9>bG4TL$E zzGTEC$F^a43Bm|s3AenjY&go`e`~pq4~VM?b96wxU;o{~uPV7@@!A_KXS5nTyEtr@ zb7IC$TQ~-yOu#w%3UIp^Q;WiTx5zLn>T|J8o^3M~`EPsI7?>CKABfSO3*u^Ep88kzN+9F|#{3q{9hW6h`Lu4}{r(yAC8`_5tRAGe+Kd z4_A5O|AFj=NF5Ky@#S(P!~(j7JlXpb0g!Q!qJ=Jl=3Te!Y&D*nWdCcGPY6XUZUUX^)mHr6u%VxjA+w#P8*39340I{QQp;HLq+LT2%I`>FTXEF}| zyk-Z0Y|qX!Lt%bCzu=G92XOm#tjV2iglL6pEso1>Z8l@H1L)~F;rau0NC(O+plcgZ zO$cHEd`ugngyM=%t8!}xd!9%HSv&n!06tg5aMl`0PnL4YC!X?aD$^|$#uvVXbk|!# z&${(v!*u5sFh7T3^FqD~_#`4l9B=?_Oi^aVU?tzKFl?85+ZAnL{E8r0X9KK;;r$7< zu!q8?4El8hbx|T!sf@B(5HMmtx>!6g#8cX&3*5!8L}^}GfT4qa`xw(S`bn6JRC_Wa zLB|lTIrU&m?^UWwn~Pcvxqn15uY?{sR*SSKokgEaNkaVM5ET%2kdsMq_6NjALh--M z`x!f0TkLcTRQ1@)Z{KesFrYc{eIL;nuaGK~g@2Uak_{=t09B`6#)R(X{;LSfyX)moTI>Mv<2Ro-;OL=Oaj!1;VDH;%Uox*$n|f2&nxf| zO*3G2Fb2u27Lk1pg&`Z)tA9vfSg@0Ib<0$DWda&>O-rQNEUuH?TTe<@6!kcH{d3am zcfW!Jvsjj+>``6yGt8!fq?Fxz*DG+Z z)kqfJ3^HwJ>@5eiOjDP+U{$ydbQxu;&5&Uv!;-+aqjdVw=0>gL`nmPKsv0O^u`#JT-cB zC)Ce$zbJK3`LMiKNXh@`9M{iG3cvydc$Vk+)%B^tVHj>O0(`56q|5lYAH_N!hqpb< z#Yh-BcR)2gtPA3=hx~iofQdf{EzsYsGPr?8|7#7@akL=yl0#6{-`0KVGFr2}XzK89 zPX;d39z)*O;$tbl9}UCB;H2CjV>C!)&9z0+{N$tqE85qRy55>*DELx;jQ^*E_AFRW zmP~l{q+c5hlfje_5a&1j;gYFiaUN_xg2(CfWznz0v9W6*8TsdF_^t#`oe|R%n7o&I zq82H}MvL6Q$hm+|F^~D^4Rr|>hi{P6``*HL2~E*(R7GFV_b zKa~PS73xpNQ~Py8VFAyCdK~TV--X1y(b~mEG#@E%i`}efS7KA?!6``D8??vSph_y+@smSF@B8H z)}jYD*mH}#E8)Z-#9zWUoY33Ghgwab-@9qs7>Z2%K(KQEPwOf+;`j}EN1sg_ktxUD z7h?dbqL*0A%D?%VuKn`}14r`2x3>AZ=%*=-%YOELW;6Ae3Qarl+;bVBCKxWL6Qk|F zUCakJcm3+H*fW4)>aY*e z?uLW;fwTCJY0_4Prc5jVA!`tu^Jk(KjB47r#ouhIq-t(RzgO_8Li+p_?t=HL#r5&` zYPiowiwWp!C|IVhDJ5>HFthu_J`dGt$Owa_YhKnO%~H6KBFYfL4>P)L4sC_8nwP4q z*#i)zESmHa^Sx+D>F6}hMws)F#+;uERGrHw0JHfa91ZxAt&x8tF*UUtc8CM-AAByB^WFAe|Xki$k$+}M94ppM{MiL>MGO0h+;iX`7ymF;xr<>9~ft0 zB5tg|`%?0n!2g6@XH|wh+jt+x?+rI>R+cYS&8cxYf}}($t06bse%DprWA40dBRAxp zANq0_kKQ^>NWbgAbP>1~$yjk5eid4c=h^=FZSR5pf^NOBkGE`A(VGyQ zJqTGUkhfpmH&nkr3M&1Q4x!ix-M;S$+YWp)ZpEzsi5T>NphQ_6Mp-TCU&gP=jUMlO zd-4t2ec#of>gG^W?VTYT_>g{_xmw{t+tNC;hUAoj&Iry3`I<201Gw%G(BTb$~;lQf_eNWDmdyvShmZB(S$R~j)D2U}#(^p_bIcsYHJ53QXoLfHhjnXvZ3=hA)?3A&PcAr5{Q z6(gICXmpDi zm#&7L{p1^ZxXt9aEG&7#g7g;mUqVSYL(WwLB!7XmC;xtW*Y`^3rnC6G=cVDc)7;vO zNiPqyv0g9be8ctjTie2i!P2y0+t&eDKgbi=v+`vAX9CWP+E8*t8G3WW8B6Caeq8G5C0jIl zYGVqk7*yY>{Scb!OEE!RxlY_Ma-jElxUJb{1Ag5o25@UWs!`&?2$%rEXreLAgi4>p zd+~wtrRQ{Z(FgMPDG$xhrXMcG+HfRKUv2zY@Elx>mXLbO1Ksy>uxvFWv6wY&3-Hd^ zEh`I=@YSB-ubG6O5$N^D!>`g0Te(OSQH_f>0eKq&Tpt>TRRc&$sr{ zU$+G<+0u+aoLp%6I2aI+9Jzc}_lMgorjjb)$4GmOk9@TYDS6<-+ z2Tu2i(BopfPyVdU>1mjx?sx8-qIN)@Qb>Fv8|-WM)}gb4$xnNB%#Jm(o3vec#0=)6 zI@WFnWM>CnEqj&Q_e#PG@|!lW<~)U{Z#T|9q6}KVMWI8NKXC=!#3)bGm445Tumwwsm-ya(FED5*K$pRk<(boId~$pPVLRy zHo7c#79$;30~);{jYF;HYpzD7U0W`RdS1FgvMw!~rRoFDx(Y6-qSNy}1$%sB)K2y= zqs1<<9mA>RBUu>$m|IPBk<{t6vlR|&uy%A@H130_H8-Z86w7i{rOP<0-6vyARsqiGqtGCOSPhwDOHHz6@OuU2Bl`IE2(&mnWR(~Zm z;K({zzxp+3?gGJ9nLDxpncbI-CY4$uJV4}xSpG}k%H5c%tVWc&ECAYpEN#l$VH*9} z9^C2=z{_>|0R`cXM32)F`5{1*0I^&$9+`St8UXBGL<7AOZQaoWMFF8r2d3@w_mXsg z%D?0z=Bc3ezDtnnO=GS&c;L^155m4K5{y|7^N)ZH%)qLrlztqsTBu{?Uo?39YUxSA(mHSFjvC(2SVCGdpoOSIS4ADHshMS^@)9~pS+43!LiOcr)B6^nA{^3)oCsN9wX==^YjTZ7?I=kVWOjXs;8AJ_N#-<2$neYY&aP0 zr*?v^#=*^i3?E#2>@ZYq)0=kaYV{4^i57=ShP~kQPBvZrQY#NAhRLYyKYQq5nU_OK zBg~mMo@qisE=(;*h{8R4#Rd-~E>z{>I3c0u6!)H3ADiLel^!-DF#>OG{0c-O-Y<8IzPi$3imBIcG zfjM?rl>JfwXdmInSdB-i3g($qq|Z}fz!_$XiRJ?~^c~3Z3SssmSU@miDtP|p%ZdIk zDhB<@Wl^W5F-0yAK3?(B-g|yneE<75z}PUKu6h@<;VYLUbGW7Axd2_y76nmt5Lm_v z0oLk=7f^Z@RtpwMu4#>OhtVx|`a6CV)oKj?ri<^jr}!W+%2u#nBl-nT%pTy983sb5YZ;jAcd14I%+ zdx8TgRQy+@SGT#m2qa=qi1D zamxPNps}mK_gQv4JlB22*!-l-5D(u&_>r_jZZ@)aC)Awkf1%1WV_;SpLMtxxEKHY6 zl6)lS9UJ=oeJMtP<=uGc65Z!Za|;t5 zus!yxL|z#C4G;@p{g%q`n!SSE#s@nv8+wl?0Ar)xZucLoSg7aF<8QLsRaQOYV~mIE z?xpbiV<^`CdT5vrtHCJVZ*f& z7na$AuVB{(&Rap5^#XCP8L=-08Elf(&5Va@XLvb&yC>)g?J?vZqeIRtMUXLUyZi3nEv#l8& znQwsd-um=1ccB|}aQ3av&2f!OG3?C+3dXD(f?AJP1!!-6_iOk9L?_1!vkY#fe6Wy4 zXBOCInspFdAO2vWW4%D1Z2BFgwUilgYUzg`p?4RpgyQmf4*O%|XIp+d;o~0CO7x=` zBnvaqL)V(Bwp5MNlo`iujcgi0=kJ6VM??6+Xe@re{vMCQnqH!zELVWIZe&&Gr zZv3FV`YRS!uE=UQ>L|Hq#-1mi-_4XDEf-& zTBTNSCa1I%-&EB^BPcn9%p*N+11qn9Wd!=7s86<8GSkgx@SsPXJH*OXFyBK`i5DO` zaOjQ*?;MP~kV|5f3HxE>g+Mjw^JuKOgM%YI#p@#T?Np;Wcf0O}w-Oh?k%*^G{s#Y3 z^?CwPL)$(>(YgEepBK&8iH|QH$z1ZhD7nAttH#=piJZBXil2rDCGNq~ zZOe;WJ`$+}8N-k++G=-#L&!n_%e?ifGv_tn@h+!{hQ>g`;v(;uN(kZWmC zcIqhL(d`+$$cFS!+{f^17jAcbTpuqeOJmjDEz8xYVVnh;bd-VvS7<^v-@lw^{M>9$ zDLGOCQ?dKmy#N8O%iXeIxyqbl>DdaTr3ncUP?U-9X7m0LfQqGsdlmnSCzn?C2B>hg7_atO3b4WN*%M7;{R(wX5(G z9|frGwC*_y>lu|?fSe;G0O`9eJ_>tG`HQOi+ATjftE%N46oWdYCUV*+8;cR2@&jUTl4txl*-3Kj zh-p#cNYs;A#_1T-B87Eix0(LQmq{e;>EJNNIpx-UI!pXVQwONrOmDP3kBN&Dvgd0N z>D&qpWiU^zM3`8y_1c*LgU??>-_fR#51B@N)IFE5GWC%W)cTyT5VQ4sjK&z9A5c|* zj^xD7dD!N}62v8D^Im+dQOKL}d@tV9#QD!X^ms}o;cjl@wULJKlV92v7Z(Q{3Hx-E z<(t@8Q0artKZ8fDfx0Vv@Nc0@dPOo1(iG1_KpikA?0sJ|iX zv%Q2|@NE+E~H&@6UHCJBIw+$hyFR0-64zPr4YJfer>qK3`KyG8%2vikAjN zY)4Pk(wlK+XJW5EYHZ0~D{?^S@pnHK<76w3Wl-U*`{Y=-m%kl@5AYpG4!t;j72aR{ z<1K02-e-`N$jx`8R3g{FPInEe!ks^Mv_YYQv$!NBLqN$r0zonUJfmTJ4yUB-c#t)> zxo1Y2^!%g93VyX_mr*!6I@Gl^r*c=IY&IbIm?qX4ov+YAmVFrqibMhr?f&Xjc3dnG zpc;Nf4Pp)yPUCQlsa1+6%6+9f1$jxM6J)c&H5Wbh+Pq`6qr1CE-sgB%-2Q7|0HB^08;s{MDE&*sXtY{Lamc&lZ)>obgr60U>4lZqTTW4WUwz z+6I2TXqaY`ZB-xrIz9QzFx&ValVK%nvSY7r9VD^_fbZ_xC%4+n4VM|h#zEio(0hiYO z^1>`(xN?UM%4{N~f8+J5gGH%6kKg1Dnm?fB^gI<-Lzd$7R2l% zjM}839#SW22h=#i-TrzBUQD3?BI_C3Bb{_}+-RuB&#~wY5$;>Lh>$L=t!0FiAfHF4 zZpnAN1-l$ecMKsyas*DapBn%E7=cvv$0zpvFyz@999cT7wJx+a^n{L5Qc`5zsH@vO zCJZ**B+Q?lm75Dc-q)u+WJW_C-#hpr6aReUxa!|D<=6ix`}4D3B(~cLm8R5NndkZG z2ABSnAsWsrKMuIr94-h)z z^x=Xs8-grr|5&{Q=)YdlRn-XES%xiw!DM1Xw=l``!@F(nAFO9TFa!Upr>DzYzkNPa zeQe~2JxkhB<8VRX4R<<2!9b(i`;yT5*)r;(hMb!wWnI=v^~dSiNm zn0h?)pm#4Q()mRRe)cRL4L|HuA|c7>kgrzO2Lh&W9!Usa8S?2N%yoN@aCLTo`@sqW zUw`J2t$6PzIz8#VIb8F^7e*(>ap$`;j#VZ5Mog&v@2?NvB@P_g$GkG*R5O3>cENwL z|Lk|2+>>KtPI50(H%OF$**J$LX3zu;jfBV6g&$h;$WeWo*&=tU^#_hj=hDB-eBY-Q z)>p7H7kqVOlX)ZjZO~mIkU~*C={j;P_xk(muS|_Ah}Zp2p7B$^DsZ#SS&KSgsJ4~p z!e2UN`B2Y~@w`KQ)??1de;xPk+z zH#t6~xUP{bw%z`7^M+42vqaWtM%v=QDPE_T~0obL+|9fB---`zdO9p zj09NV?9PSy>bnB`zK%J?fe%*VO7YKXi(`yC4z`=+{hLrvs#}@h4N7RiSblFaJ75CD z@~0@R4GwRKG}$)Qfqh%H1ow=02$_g@@y*z8RrhzDGH3QxfvcJpI7>xEMJtQOjh*o! zIN|c?@0YZZv}DiK(9-&Vy#*tXHv!3sj_3w<^lNq%2U8E=WCw%|%;N_i5OVsH{uBYH z1}2a`{ZE-7kP3jf`Jcr8=OzB<^*=3&{kSG!9y5Ik+T2-r7tyQ&Z zuD#DXzusN*THp8ohV`v^Y;4uo$zx}oHActrv6rtM8++^6*w}gJ`PXkdWo+z8x;H-V zU;h@p_nI@t#+H_X>+NG>Teppk?cVKQ&+Zu;`}7OP#*Q2bp8qU;PYJ&H&B61NSB?GL z1*^vJhx}&8*N*+s)atQWx~8P*j(?vHW4kF|-QPlQ@>Tu$$!8b)Q{F;v%A<2WF3jUR zxYwKX`t%#nJ9K=Oey>7Wbp48@!FF-~IKH9uyal8Y{``ALi@rO)xG?E0FHX}pldqt> zo>}Zu+WpDn{kci6H#Oz;w&OkIJ?YI)(>2|jpQPiYpC;Wu4mv3PX9o+M_Hn#RX?Tkl zj(dx{CcHl7(VM1wl;+Z<0~+TxZ}+Y^c%FCc+hg9||8|l$ zw`IGxbkVrC|8Gz54m`Hn+yBTK@6h9`y^C+%?De)yM;I}$Q{L>RY47r5=Xo#l#=Lt! zzs8$6YlnBuYd3f=_f~m#e*9!_?`KcsXIH%BJbHJ#kBz{Ij>zxwL+5!fJwE2$^JilhOi*|?1_p z$M=xQ#kX$p4t{mDcku65dxyWe%G>va6TAWG0Ru@2Ri>OzVW>VQYiihKSQo{fPsF^}ti)(R&T|ldgWt}cdk*=pFN-aF8QDRUs~fG{nu6A(SI59E}`=5miM2^G$8$J zUQg*W{h-@VE0izGf%2lf{q)a?(ZA#D8Sj!iF7S>$x5_(0Y4894S9=TFC%xN0athOb z>^rNNXMs;de}s{LkML)F&S6|phTZvzlfCWhc6ism?QHLUlE?jo*=>J#inoKx**pH| zWGQ_gA1>cOf1o>phcJD@XE*UG@JAXbr|$e?KP}_|`EOr0wd?M`c?;J_7&y2~+6LEx&)NcjuSNV6W&;kUne!>BClFGob(O|FPD)_pjD+`iOaUzxo_bA2xRv*&bpS z{2&uwe*&Emm9vALvIu;Lzi%Se__D;VX`*kC7yD|8kNdMC-v}S1Lpu8Tpe%^R=)OJp?}~FA`J8nxqAf@# z#D(Y-G0^Ax{c$cIgSh;-;46_1<0_|#yu`IQGCf>&iSiL;FqM~FzH>Pl%01vy&>Y^U zv^X!B7rg7|fp}{P<@ANpPGN5mQbi}uKCb07F#po7(;IFA%KfviW z@E_u^ar*71AL@bldowD4iN950$h0sw@%)9}#D|EVr@><+s|Zu(mCG{!Io?UWC+TB| z6{R^p@u9)|zDgVH;UdawVSf5;pkdIPxOkEH z4(&wng){@4W4b4vryL{uk?{!6*^W5Rx$#Sh);~bqL6-;0b-pi-YMyjI(hv#<^01mgZ$KM`Q7L|o@e+C=!wzh=f!oSK+jbk`CG)pGrq6o zSeJZwA2Bi0>gR>`P=Dj+a$Jnq7(c|WA!hP<#BWF^zbES-)kP_89R%$c95*jf%q;i` z=<8A4nadL{Z~Qtb+B*g%muNx$5y(boci@yt!Xd$bXf0-A|wG-S{hKFfI57G5d>t@r-zU9>_UHk0g76gEs&71OM%QordCu17BXv z^_|22O!-qxcGuQT_(%Ro@6x+Bah`{t z^szqp)iKWF#lL+zzsvk9`E%Nwudn~nb)<_zCL#ZXA*U7OkG8HI8)gXWv%F&@`@@h8 z@qQPzQSCT;hj;7mQ{9N(#eK$K!@s~j%3s)HKnG#s=YIyryD|AwoeAwtXpcJfEuxLE zLEYebqG$S)S;h$Yqppego$;sGR@fHjE$j_g(7B)^e24DM(Kk2z!kMIB6r+Mi)ETHA zh&l<{qo8v~o+dt@q7T-HAQM3cXo}$<%KtRVW7fNp+AM)B z^zJD61=v3P>5dIM0{(rPRs5rJiEw5N#8J=_9FafqFWcY!)K+&Z)k~*Oo%L=czksxT zyCc0Mzks?dY?Ap`@mKU;$Ume(vVF!N#(#i6r3wExL%Mg*pP#^WD%MN#OE380^{kU< zN4$;e&NBZ2{*vAcUnug5m-D_3hWNw3K$j_h_*%4EAO1RF{|)**MLO^=tGEpsItknK z`KSD$mx2Dx1^h?&OL(&!{A`^zL=uRWCm`wC|(Ol8^tJHGUaLX-y ze_h_-UE*KX3*>*}FP*_<$WgL0)S>tP-5L*dN7U8vL)mln_nqtKPh}FyCg>u{DzrnP zE(={1`1|P#A0zS(Y(S=k&gZGjyqL;Pl%Xg~4}6(^8V!{?|kLd$0Y6NxsmXO0~xUBnU`Qy{Luk7AMmwcl|pKy^<)Ci3|i zpl*v;WT$@|h?s?C9;MIiF~n!+^Aw#Bvgo0{8^%`gSc}`vjDPo~d5_n+}O_jMr8 zMXLLtc1n5p^)ES21syTo@vWSe#CPny;5%_IIEwsH&q7@fdHQugs(;cy)Cp1l1TJV7 ziFonz39l(V8Jmzs&L{fL*A+pd-><}JQ~h($oBRopH)_9J1E&8Ldy`k9{!Mvt{af%0 z8HDGYzercm5@Eyd20Z)UiFcrD;~!d>op?U-7J2$~wD&*SDbQDRgnnT=1@(ES|1tGr zgTcnrNFV%{V)+$0*|y~OtzAK%lL=i`#2-dCqV;f86O`%IYzrrYIw`P zTt)pylgH@0fA)1E=y#0emyhm{CS4keKB(`)|Dg>Hv=ARpfVPle%?W+~-d#%+hmyT; zTBh{F`@Y?V`G)sp9+h`Ad=PD%pE!o;N$-M(UCT4Dm-lK&r~byt-`E+n_0T(h+ozmv zKpW=~(y7oE-k0g8bJP8hJ|&&-ek>j8&z&M3$VU;aG7o4!#Qmt3V) z!MS?P{2cdbo%q^imu)_UV)U1xUp`J-gjtLx8yOhhiKj2qWy&k<9p9!wJK6M~1pUd9 zE(G7o7#ir(=}A$ZlRTPvV48_`%IirHr-^*y>BR9#dnUeTxhHrBEiygwxtxb`Kar0A zZa}kNHW}VAVk^HNo>RWzxlE6IuF%FZmG8;t_)fhJV+NrsF?-cx8>J!osQh-(pq~ou zr&LD^>k5fH{paGFv?KbvgbZa`zk;1A}zG>eswjaf3mlHikY0*858@v63qAgjW#pM4i?lb+Q0~oJ{_5$SX|EP_QXuIL(;%%{9XGc5ogQqb4 zhaUIeKTO}D-~Pq_*XiC1-%jnEL3@Wt4{6`~7i+nHfo)2njd50V3HNmOL%SF6qrZdn z$CiHBDEcV4e}M26^l^I?waq;L73X?;-uyh~J$#0COQQeoKRrQ9f80*<$wuf;l74DG z3h-Q+V0Vg9paC6HPRXA__aSy_@fOfl%?Mmggk&1`ax1@W^5y$!E%t~ zC4Y;wkmfxx{Xtt#L_d$&L7MbK8`bqca~8Kh%k-f?WZz$}@n$#9c-OyEp`UaN`or{- zuB!SY(_=m-eq_G*oODdmAIMmyiTffX{YRS>#%S*S^oib4w4+P-LRRQUm_BLNyWwZ4 z4VyIx-=$Mv%rYF)9GxmYL1(?h4$@C+SkfxaakpHAUOOyjwea}W8TWVhw@1kw_2=TM) znyuc<89TfefqonOZRnS2)|?Cag+d8dxl?<^9TupxyVv>$muQkK`*u`q55Lx^m@rZR9p_ z$V$*3z7xnu=RecvPkRO#;dui8pgcw1zW(@q5wja(iKwGFk|M^UB`ouZ!+PC{-W?A4O z#u3i21wnJ151D>BeuE7||F5O7=RBU9`hwB-FYFfm&?v{yUx{(HoNq`!jfY|XnL)oo zv!Fe8q<4JUef^9*15cv-qPhiSEaG|Le=tt}+P6`CMDgc{m+zx_O+1#1>_^Za+HWHM zS;ga%{rEqQVS*;cTE<*ppF9fuQMY6ICHsYa-u{8pxZdRJPJk!r82XzrCqVd3K?CXv zqWl%I0v4J2&$6)LE6xMgXUX^7PUG+~@6E4sQ-3+?-c+`tyn>Gm>*!P$N4$fv9GB48 zY{V6UcD7%#1=RUpDy4-q(KdiK0v-O5ZU?lI%u1@i|Rc(`olW8oR;xiBO~nZ2#W|kI=X4#mfkyYE zG(=kdF}feUtKl`oBX~dHT}~_Uy~<;lMr!()L-v%~elF)@e;)BBzVT_K`!f9soe4fb zxBL!&n@k%t)3;AjyRoMKLc_C6g9aO>L(W(8j^Hcvn2{I05Bo2AliYvgry21n=n3-* z($wOWpfyTIe4qK91aNi*~r;X66;jIdSni}@QKBNOzW9`yTw{gDHH z|Mg6AzCZ#SC4GfXl`pw|ltyFpOfMfwH>6$dzZQMjl*W6}M<3`?+;1fK(B~7CEvDzi z?k_ED_b|4B@X(GQ5VF7+3F7q$@Bkb4eJf$E;A~5$DvtXy_5JptaqjJqr=zY9?u^HPv9-dadZdz9=wNlwPg$POVTgs zj?0B(lmG|wH{#pkM_t;=J?4euGvYDgI68yBD93va`big0$6k|O zhyi$wIHxsT7RG0>1{BZhr1hON-^TK`eqMsV^gJ?oHPOqq0AA&?f#Tlx!B0@0N#4_V zz%zdC(<947K4)H}bP~^CBf)d+Gu4-*+MuvcBSy!W*Z$-Op#yzdWCCS|$WM({E387TE4M-)bU*!tkkr8eu;Kk+=QOJF(@BQI#c+5jLr<1sWkf6X3P@Lb=ZaX zq;!INKF?EcAQ~UyHV+lI@Y}eI^>oVbNO`GgR&ecO8s(v4oS;wQgWnCV1D+qIdrv?g z;@?H-$M37}wQ*gHwrGx#|9!9ymVG>*DLaWS33Gv8Oir5T;=P8CDRmi%pRBUA1V1rc zfU%mEdL7Dv=rcac)=SwIl97W25vH z#IKe7=>7hjp3ab;y!a^Zhx{t{Io3iOj_9i~9QA7j`Mh)5lm_OFqA!ZtfMWADV|_Pj z`WIm>Oj;KaW0=t|BKxm^){u4s<{?Jm^Ej6N_wjq;8;rHt^@6QD)|kf|i*d{1$a7h- z2Gj=bQ;T9GDLtYY{aA~{%Zq<=9j$dqYrJ8tH<5-o%KdqN?~i%l_r9`*$J_Fna~S^+ z@)6gqD(=gZdygn(dONw`o(l@bm zsIL`$uNY?#_P?TER{ZFFjEnZycp&|Ot-+QQ9MtP7@L>7BaH~JBoyH&XmNV+7!uF}Z7W$vUKgL?z|L^*>$Ur0f8rFc}aU~?ri2rEJ1>}D1kDu*bN@L}D z{DU;+Q@jt1!2gBUZQ-%G3HfVi7Z|1Eqrm}s6{MHNf13P}Km69^2Z=8lUm%ak3i-mg zM0$3~T^G>UL|W4q<2h9RF%AvuDtvvFx9i%i!J0La?T5UlkL8GPi1QrXm(mO{D9k@- zq92bFrLkR*u{@qp@DG_{EDZY8F(&^Q=~9A!*aGuUV}D5(L6eYe`C}XlHND*Y1GnG@ zp71@^^Hs-@3;yBvus#gdi`@N#w8mzt{FD3}Whdwebt9rn(5E{_>G`@iX&v0=KqcUFBl_x z^8=@cNhV$?%e=`0-xwq#H z$#MJwhuFAO8e0e*NboP^Bl3^zKfT-%@{1j9co*eHa8&sh^oK`{{_|K%ihFMRpga#C zt^=q8gzGa=y-v$N~1QBOsk zRjh%>-v~Z}?_e`G|8}rG6paZ7|GP{0{}}C7a!%JDfuGj?CB4-7f7Ep^yBBL~8*HC2 z!&=Q(|G);GhX9*IoUH19=wk!>hYTY9kK~`)A0%TTwVwuqz$GqhGc@096fB2rn3&wc!JUFTw@)+4N{AbXgkgtZvH2o3sN9+k1B=}MI=eCYZ z$N4?3+YsNO>;nwKd35r83L$@t|EDz7Z$kO&>VF!ZjQXv4*T)F{fPMj<;(Nir$UlpJ z=)jdP^XG1&Z9sh=_u%_b*TuU+2UYzy@~_nY1f6zrmVJi#Tuw6&|FC<+F*p6HKd(gP z0a#%DI~=jk53f-GpOjnw1KfA-I$Y%cVYK2U%~Ga z4#8Zi>#4pAT@kjHU;b+QhmWI{KZ$o8f7)*(Tza(UfZ*TfDK1aB{KK4h)ZyiFPqzQC zzDx5E#lA+Iza)S72Be8{M$FGd8p8i*<i zHFj^MeXy7ZqFF5~!}>1IW0Y)E$3MQ8ei8rK^`E4iDK8Drayv&19|_Mi`)B_a@PG9y z&t)6F?~AMbxw8`Of{(DiOR*l$gA{n%@K5m)Ffhws!jsZXU=Z@J(Gx+3#J9jBjeq7V z;2-N1U|j;lHv0Om-@g&hqWn!U+9- zFQWC1W96QZL5zP&BZYrq|5oy*bR&GsIv%|vzwe(zUbECba~rL1jy_1VXNCQeqJNUg zd&n1S287S(9%DlH!8_Kk!ak3Pj}rM2otO`cK2)qF#d!qZi}ZM}BwC9GeX-EVL>dB1 zn_~d( zXbt2K|H1Q7HGSjgKg17x@uB{w@}csTytdLI&`30+O$Tih;3Hf|Ef}j{9KS$oO>=BV zbpnB*QEtQwr>%~EP_AR+9|V3yk2&!ZbI`$y^$&B_sV^YGx0*NE2Kz?CTw0NzD6_0& zM|r64$=C9ECVhl4zpv6=6#KOA)I8K{@vZK@d{1ic3i#8H0h7NGX{$%+omAYy?<7AW zeVf3U-q)s=_(n=E%tNDB;j_g1JYNRmA7l9?@x3PD_v`zWacS?GR@0I5|Jk)#f zI6b0Geb2v^pZn(l&lvysusZ%VL0=}1Cf+UX7vNpbqb@I*@Az}@OnlNh+*5DJHU7CY zy%9|@oXe)0^ZS73H)0GpbRk%yIi~0KILEP7zl-r%LYIu`#CVVJ1s(Bkgzi&c44%>0 zf6M`(^7Dhn`43j~#`9G9*ImcyO2;q!KE|izH%a=%{D=2}cl=>4h(G^@`AE(INv4r3 zyYWs)pTI*L&9p{17~T!jl+w`Mll*gtSMnb3_Xu7w=f~&X|KE20Q z)mbSYF1vl4GI2}h#d5`3@|53?%Djtnx^FfA%?bmOQADG}LrhQN_oMfTN3D!a@5k?h zz9=nz#=NJ^e`5@2PAGkAp8uG{RH9RrOGJ~Hd!W!1*^r3IbUce^y1eb~SiN6Zrm;tK-hyTj>ZO2slE&PtZ@S6Y=Jsr3>9(%9;VX*LP#R zlK$jPr1$Tlc=J(p21_(_T_-bq;8yldB{ zQ*~HXd5`kBkg)z0`aO<%9`D^nWr0mumWgMaM~$3H`nb&WlVwoS&qnWn=MV$G&ie$_ zcmC^QZ+gB;%#qgo{M0nz_aNeH#14yn>=PZ)l8LQJ*9%~hKd<6)Gvgmm(=VI-xGLC- z>JOLZCNp;rvyl_UJU;|Iw~g6Ue9Sxu`NerrzaO@1*u~DpnP4rfiPr<;obH{)!uv$c zPkwACt-%$vqZO||hx%Ut>qcxu=t6ZzV21U}$R~YNtb1pp?|7%z>zz*J%jf+4)0n0T zu#b4lwh8gBUS9U!i*)E%NRImaEp0NW8}lA?pX>GJPmkemFbeD&2<)Nvmibipo{3-6 zeF@*t#_aDElA|`CbozWOr#=U4V=~ZO5a!%YQ3m_!__D{c482W!8~Bn3HIRq5sO)$( z`z)=DYrvwAPpE+o!EX+FA{~S7r|Z9vFJwS#pZ;J7xO6fveVu#nU^mAJomIT z#q=VOgPBh1qd~bk@gW)iIjSq5Zk4wlYfX=i#^7ERY2?AM+`GbFf}@PHe4fHDw;coP z!Bk(!fc)1l7SjEce2VaCl7;Dhkv!yYs!3PK#KH#!GV*O;>KB1OVj`mB3HT+MHu*e1 zojGVKB*U!qvz}XERfsOTbQ$YlJ@Uo*@#|4n)3)bW+LSCScb+W+zrBe1cls085nex) zru+5d8R)7kYx&%yhc@q+zU{z$j)UeW-%0a?KM}O=82yj6464F}(`m?uR7JB%riSzb z%s)wG?^p81Jy!BC_1EgLHDiN)P*|X079jz4PREFyF{3eTfgek9mWb z>(}2-;Ge34=gd?8T0*VeZ+8$Bm?cT z4B#6s-An6E(H_8%pPFXk8f#1h^U@Q~l=;{EJecQ<^?NS9RoYM3f;O2>^<143Af9L8 ziucra)N6jHQv6Bo8e8V|4yk;{I>x;I1!0wiDb@nQ9N~NZ>;&GkS+_o{E)DFld*d&i z;oE?2ZfQw*2>dHK5>`d@|4$i)u-z)uLj zox}g|8aWiFiZx0S7^;}kHOl8ZKY5b3WBrV`^J;&MXvo~^TUGy4cu(M@Vwm^ayzf(f zh?VojKmJ;a`uHcQLx=^h{()evSyE>AP{*hY|4Ok_ z^(|RO*vK>&{-V752Kk_j_y;zQb-pjYWi!{6wdz>nkI{3a2{weWKIwsX% znx02oPPy<8@Y4IAc)F;EA?|#^kF2M-lge-M>n5EK?+3pB1#eo<-*VYqME@Co?8Ot& zl;yj#o=2Fb$S9Y8^V3PkUtd46!Jo_ZE!(|29z2EH972pWIxn7adjt8Yi*EAU5A5)d z)0l`qb72z6E5f_pd+GS=b=C%d^beqI8Ept?I};eE9Z^Q?`|CBVw^-B54*z)C3H%kG zpQ-1Hyz;)ES8gHx6f=N!#z(~Y23@tmpW7S`nfEr*$pLLcH~-e@VcSg|_?!E^Q~Zh| zLwk&~(+u&?)&7}L{szAPhMzOIP`@x%yS-`uG#iHu%f2Khn(7e|zjK{SMoU z24}^NO!?a4FY}queP`tdppVBCU6&JI<>E%j=U3nI>8#AHu$UQHI9E_+wkRU zi@(e#<4NH{u(;%@YmoV^I$l)(Pu$>_K|2H+mZ1H?E;guV;cV4*F}35r0wmcrp@im)Th66 zNwDujz<+)CTb8Y%9Fp?N#@8+n8UHHuA8iH~rR^=I(^+b7!#<1nRvyPw3H~YmIX{Ng z%OS)+i2u`kw9s3l{u8h8-+QS2z_ix8NFzK-?ai|92R1(bS?&Mv@}2#Usr@t5XFJ`@ zFQ1(0TKXSVCMNt@mgK+F_EtMYdkTlxlP!ep&G&5Leh8Uod;O2nH2Z&@?&|L4#4wO) z(Xt`b|6KJiA2&7r6nncJ{IYqU3mJQyb9*zrk9{tAOrxZuD*mb-sA=KajDG=KO}@)? zU!q;sqqOsK_{aAy}k0g2%;ob;UA6R0Hfg53 zlMDZ_|0U^vxXpp~en{I}Pt{d1zU|_h_2bnl!C#BB9^1TnpLjn7=g=OMceMB`?E?wi zG~*wzUmM1XKp*|RP{_Y1>8QHO?ag06dozuBM_(ml6EE&ds4vi@hpKM`&s&$!qZ1;w}W5$(;`w}i*bYQ_qN_?y}vG;|98gFVc^ zTW%~B{mlJ+mdup&W&G3XU$OnxRQ(Uy9I&?`+K^?8jpw4hS?u$wp}P$JrtxpUGt&P8 z^3l*K%ScUIy_Vl)S}GWS%VXTAzbd}BqDc;Z+`~46_U7GE8Lsda;$M0DPt~|5{fdgS z=~~u**sf7uC0keh5AOp;doRK7+m4e(d-FYi9&c}sX!Q9B@i+M&O<5rPPn!Nm`BW)~ zqztljA9NJKpU3}5?f>xK*dN-u&4FYIKXv0T$J?7#TtfV1{nubFbOv#P4gNN`sI-*2 zR(!sk7Vt+|CFWnKv5w%4kHkBk$Mm1t%+TI{3)+vcKc{3TrgLs@CR@AWCGqxVnJ%0s z@sHXW;%P_sZSYs~DRNzv3}yV&%3qmZ8UH!beav^;{|MR~A{^xR)pIevIcjfC+O8gd zMZOwas2+bkZCaV7>A6z2iSLW}9~u80&ztcsx$^?=DDA&zJ;u$@-YjSm$E5x%_-kyf zdi+sFA@l9MS-GCGIn#*$4|JbplumCNFaERS_xbVn=VBp-84M=9OYf%kW{P{X?ac;! zOzq7o{bu~la<0e!EB+_&@u@M8Y*X@iSU&i&$d7-d|G?||cb@J0eoa}Z#yuDVjrpP4 zeYQioB3@$m9?F?g|pqn;4 zig(b4!0inR&f>XxMEZAp^yFY}Xwnx7{>8B^{6+p^99VdrhGWLFG+Fd$5AREEyMX-& zj};H`OxCAVdsi_wU1Ka9=J4{G9aLww;VBdUkj_YNSpPHH|FiSX){QWY47jhSw5K@M`asN@ znpZ#<^1@mo$^PUVn8tLu=ywqxp{!!*M&%r@*fH(xLP`7+9+SFp@X$_ zP`BWEzE$%{y(W4w z1~q*REPAe?N%*}?e?hpJ-ZkBq)3kX3%vZt9bU#gR z_?bQZzAP-?FHBD{Y*X}v*5{;s3dyJO9;#yf`NTRZmV6cQjhQk~WSMy1nznp8oX7`s z)Q}frPHX>@PP{YN3m{cqTDr0@GCt4eC(7t;NVf??(|sfDk~~X%8|-=UQ`iFovRE|M zWu*LUXyTYCAHE?*R_RCdR3ew0c^0Kr{*Lz_!T!Ot_Y3cH%KItXwPDHWRCgZ9s~SwB z^s1vVl6%=S1Ap^`*bkEY_b>W;B2|}%p2p^JDJ##!_qlwIp5I_U&R-N;pkgnudHD73PonMwG5T-)z~*>8WX?JyYMU_PRV@8hVan z??GUUy-o%0@)3KZMtfeDr=dOkvgNM9r}p?8*pmNwwZL5Xe{qc?(e+>?gKqL+hh2I7 zvCGE}qX5&`{>wW25ff27)YAUrQX7io=c>fPI<1Uy!8#q{BZzm*`;RMv==FN1Lk5x^ zgylh=d~Mz>=A#uchWFUdnVaX z3WjCr%#Z=^bsg;G{xM*gg}3SXpg-|?;E&j}Y&&U!=Nj=zI6pr%Z3ML7gT>zTJoQ8WSIVFEYxZMV979=T@D-=K81E_;<*rj?7w}@D z8G0^bK=r=I>Gv!B{^W((@TOZt`qQzoUAs1&O8MMOw7rw~=RH*En0qhDa@e&dzwZ#= ze!tcHcTz%f7>vHK5tQAWPKQZG6B&Z|Y@Itk)QhI-S;ITc0d-G*o#n4?lU{O{I}+CyEKDzAH@!cFoM3 zgS3J=UNOIKu+>x=4SL)DH*MN+6?ung(6}X9D+2Aa3tP`N=>XyVUVltqn>H5G+^X+s zU-vXJC}UgnEz6eL33c-C_ot`Ou3Wx7*G_(_e^akJiNB?v%EyZxUfFnLr^D^V)NYUQ zR;msR=BM6cSI^hzr6n$DJZLc^oz>p2Aip$PM)?`)goJ5qo(}ivps!ZyzZ(tQvu&a- zS=-`g`(9OaLdNU|mZvwc51>9l^cl2Y?w9Z@CCkOCu&pi~=xu$v%k{hY$?q3s0O{ao zQTA6ye+gMO<=fMAM)yp2uPIt9&L@!d!;nACQ9hUC{+#UlyQv4NJ*d}3b*`#()ch+> z*Pwqz%8x+S&(XN8jr6;l$J$q{V_ngqBKTBEd)xV&c;WT{8c#^i-x0L;)ZqWi+dx^~3PD_SNBj(Kpa>)j&qV42fAP@?J4B4vM(JnP~=E8V7P zZ92a!{&mj-nLmOt=E1n0Tt2{0GoRm(=@-DHkoiCU+*5jr9OH%iy5PNE*9kF*s)wsj1 zu%`#@$5EQT6xvJg^W2G zGe>*KU$*~z-op_4rP=J2roMyy3Ge>X6TJCJ+PiB;p~pzKf+b{6-_D*p<6ZL~&!K%P zSMmOD$Nr6IqW$31Jkzd^TJ1H5J;(fX#a?rV9$)QUwr^8+b)bNJ6MCei!SW}bc^`Dz zY_4SNUn zEcbfM!k6Z1=JbcnzTb%aV;Cs@A<#$I{hl|j=Y1se?2Q`Al5_xj+uH7@9e$_i2lj!y z_5G*vetwPYL#Wteral#VUynKRszv@Wd1vc0_Dx*Aa+|-;@Uvv|1?`#jhID}UH7=Nj z&>!s4j`FM?d1PafJKvm`i}Y)ee;yqrx@R|Tr}*nk^83ksdrfw#>Ii)I%@3^e28$Ek zzR#^?KW_Y1v3dEu!%wc_vJm^j&uy`pXI+&ng^h}1Rq3XuQ;Ga(k6oMcUpIylF(2yp z_x{CN5Bp-vvamgey<_j;%kJCcO`kMNb%v98-{0ta=#`yr9e&E+Gx)OoB!7yHg`JG* zu@d=P#(z2td)ubH3$NS4%%o3A`OCVHe%`43P32^R zdeNr*dEe{{wtKh!{%Ib{@5o-oxrN=5&Ompr{fV=^9p}t&%m(=%f&FLbO)|fR?cXH- z8t`}?KG>_4Xt)MNkVm`;^PQ9oYvyK3d{ z_t)6cg82H9yDsqCo+$3p*<;o{wAUQ?(rS+T5lf(-C?@}$@kBlHPmjTE@k)QstYc~4 zRV#nmH%w~(Pt|SI`^a9eBfp(m_Gics<;HCvr1lyrM?^nie)&h`OeFsc>W}|~pKq|d zS9L)v|G;M+&cI&SCb$27CcFJN%$xW{doTK)?)=2b9@_XtTDguW`@b{h?RmpF+-A>p z$-MTjln;^o8;Jjf{98Om@?Yh*o2AHH)dz8X)Vls3`9ZX;p^xw2KT$u-(~@%U|&|dE{@x&bI$2w13if`0FczViM*wft+*=4HAf`3LRhDfTbpB%kM&e^BE0Z*Az%zsdHMT3uyp&6Vq>!27do#xJxTZKmwz%Y<#L+*|2=

1o$pjC|H*j#uWRoIFX->v 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 diff --git a/nym-wallet/src-tauri/icons/tray_icon.png b/nym-wallet/src-tauri/icons/tray_icon.png index c8c1b37414bd3270cbc0548ebf28c0193bef0353..1a52bb52589020dd9766dceb860773a2b359e430 100644 GIT binary patch delta 3581 zcmVYiwLc703T)=I(mek0g!rXi5?%rHSK&QYhuA zBJ&1`hs1}ayu>FYKzsm%1Of>~m8g*T1VVrW;_(H6Wbp<;B_w2!c=SP_#EIP+YPU&E z(md_h`?@m+ow;{|3uJd=-*s;8o!^JX>)yS-GjrygGv}N;1Allt9*@W4@pwEQkH_Qj zcsw4D$K&yMJRXn7P ziaQ|7gMD2S;70Yu#{W@i?8Jm=ecXHC!0lQU?&d%rf-$2Y>?Ts%i4bH)X%b+W8?bm5 zu=7M}mSLxw&41>jr)OtRU8`?4=+QO|QFm5;*tKg{fBBZ-`40=+^h)enjvg*h5` z&X9iKs4cEJtJ-Oexnx1VXKTJcKi&8dFk4^M>J1V94eDZ1aZa*nWb6eW(n@_?X*KFP z!CL7I!vMGX2-NDOjKR$Ds7hNEh?G*18(^$GZjF6let&xE2l3Tn7J9A?df(BpS{pfd z@Q^O{eO)R28n#vj-o!H^>yBSL7|c#9lGa)}!C3o~Fl>DE>6w{lHo_$Iw&TUyotzB! z9X|XHO~H3bsauQ*Ly87Hw;u2{u%aPU5NNjcsW5E3dwzQQkF7pLkLcMTxTVhIWH@r@ z$h)-)et!sp489?vzz_caMNEP=VHkpFZx9rJH8MK-S~vL7D&c0*0Flk#f9S~DRG@#% z%+dfce}2|?1G6Eeq|YwfhI!+Yk54_k+KEoP0JsNz-q_fIJ{>$npe-EPIY0Q3V^%U);sPP+R|5k zjEMHe%U(bG*8{UrA^;}*-IFJ#UK96oVrC$*!5JSPcaGlXLSIE`Jz`yi#}EFxA=1_u ztACV!_1=SrUmKeOojL)SoSclMlHM!p*^I{WZWK&PkuInYBz+}mlbSDUH{-P5S(jveXS(H?WS~hak`&Qkm9+EO*BRaP`zgec4!?@P{Rn$Hc>iu z1;x24%$A`0aEfJcS>xinZUYyk%(soJBZIF1eZ^qtr{_`~5=*xLC_r5?xbV^4xcHIh zxtZ%aa;=@|)IXpuh1mT4GuZmoQ-9zDG|BbOS>_ zJMWygw31o@U`@~$n^?SWCoX(?1bDUqteDPY-(j{%Ij#)|7eBTKjoZt(^%GA-g4NYS z0s2A{X2o_EfIf1XEA6CK04P?um)^G%SPCP@E$G4nlG@r%w%r2sObyF#AAiQ>V@tT@ z8_ysdE_CG+h+sNLu$?A~HGq4Tl#|X}z4ULB-Jpa!ia=BJc6XhrmQ809K>!yNa4E#4 z_w0n(8i2y|s_Fa*z-m=aU0z#MD4cI%&wW!UoNc<#o$NXq7UAwb)b1RFS}|P^q^o_pTNR+DAY7YXHum%7s&D$)Ygmkhb@ ziKm}8vLyf#C+bru|8)s;DU2Mkb`SwIi~49knz!|V8sZ%yS7c8BN(#Z#brff-KuOF% zo9hWclVP_8Xp9Yjs@6pc^F+1;AP)7aszv#)SAag{mXqzj0O$Ozynk#{v<%GG_-9W5 zTo^VWlpk6ILUw_lwkQJX7PS`-!Vc)yb>I00zvFQK`o z4x;S$_^by5yF59b2iQQ_CLS_X(mV;9FSNhu((30s~%&B5O;D7J{%(jBd5#(Vxc#{YK zt#LW1P@Jow@MM=^;f96TS#-GpSq@&^)G@={Bm%%|jtELkVfk1T7VhW=Sg9)@)b7bo zdN|unbfwvn9$xIKGM8O?m{GkuKk4C23&7Q|@bP7+3(Y7f#9gL`cjY5JoM{1Qg@p^x zG|)FAVPV}qEPqTA7Phd%0qRHcksi*r0L1BGnaSlxmYjX>7#8M86F5I02kGI=3xL>h z2?~`SS#!=+(w7zbrmF}R>X8=clpe1AS9&VM{ZGV`Tf$B>)Mb~-(xcU76mmWU0 z1X?yOHqbdNY*4#%AW9E6v+D=sLIb#0yCpsR&ngP@weG^gb$(8IIIB^?j5h0kabe*_ zgHl{A+_6l|Fwn11d+|^l5Xz<<$c+HBHf<}h0 zi}GJu>0z{IReD%T$Fnr^b14ASdTU71!&3RHI(#8c53jhlha-W>(B#jb07N@|MQ;u1 zGYuCZ>?S?jIM9#q)?y~p!}$^b(EvpS{cIDZ6MtQVg z9LifmOVLW8lTMFMJ(BF8mOTeDM(zZ_RSTCD`u}(tknP?&^?-zpS1eXuwHeE=+Zu(D zyWRKCZAqJMuOW8=AdZ~v*CAR1r%fPEz_eC0qC67G~B3azU? z>VKQ9LM?{Zx4-ZPliGa(pA(XmQ}W#5^VhhBTi0>-T3|51wlB=1xx0kw_+~(A2HPZk zsLntwE*$N|VBl66TfV*Ewxdh)OD5?S0IA^GQUJ=-SpbR(yx66Ub{jbY)L`89;c1-z z*d8psZ5U>_aLou|*JBtm3a9JX_P{(!kAGc>Hj7SiRZ)=Ua&Fo5@zzhxMk=(OEseK% zaeMs<-Dq6V`o&1FwmmS1E#Fx{<4`}0Y(~9ijZMx`?0f7A^yLtCbGipVy#gS}zR(TE z;4iOZvt>oFgV(W}xQ+gf*;;^J4ADP%sdKeZCp0-9<7OEAbPK>$6A*N*e%-`f!+#k7 zBqd<75Ao*i|5n>)5Fxq=2EP{qki5HVcw>TZyn%mfJtX{yNh||bi8|L4xSyvTjCva5 z0Fmai_F4Bs%7Y&_^|^u8oQ*zI(sOVmDwWoK=Hrt?y#(}*Irra6GB-=+=-9Jooiq}u z+4xsp0D1v4NFnOuxX;sZbZmT#DSul&&hn6y!w_??AyU>_W2`;tKF7!Pu9sv6Y9czt zGPKP{t+ldOdn;n@Nd`GMTHbA$eQ)frBdm1&RN$ar>ak!h~h9d{0^8U|B?D7^-TkjVr$u&=JygjH-9?1br0^P z_m92cVHN1Rx+@%K03=uyJ`_a~-ZlN=n5mxH0P{CRhM)XdZmjR}9Ef&;)#{`%BoimngJ?$ z?&Qfoht2vs0bF*f%$tLbNFQk(xWTW7jkm^wFB7UK%jzC_=o1FxHS(=8Za?ocHVybj{frCm$ahy z$giz6mgP(*a#6#)#tbYoyFO4_lUwUEe`V#|+0ULib7r|^N_+VL8`bew8?t}&$lEE< zpCLuBB*`3FQ3xUm?0Bo)d;@%1Q8l&;WV0NZ!iZ&>lal-so050?&*`unHbgH#B7Uz^jBqP^=Xzn zrRHn-YDH;33$0w?^7Qqke-{=OGtORHC<~oTtW2}ze4Z7fRWU3gW_xSD@-)01xcRTa zVDL$4?kETA-4Q`|PH!0(cV_HmK@|pcEh-r+>~WzeO(y!Ur2s6J?V^rH@})exlCsdw z8gl`j+2dJLO2@ErpdF?EI@6^(6N@4sN4IU--(4|50H&3mT3i2&=ZK>yf@$|TxSyX? zRoIZ_6SaNEFJkq2rVz40fSDoeLj-cP4zGW}K}c=s!_?voA>_ZunBDwVuj{Rg#LEp` zZ84!@NX^UYJlBe2f~mO2IFu}CJk+w%BB^}B{nW{uhwH3aMpsQ1&@&NjN5Ic%^R{!` zb8l62VtXrZ#SXO*!O_*d>cem1;r^;N`Sex{fAQ3$9m{EQj|0}*&}Z(ma*%vyu7frD2wtqSB6T($Vf)t)(##qPX8lL ze(3Q}KBG+&&i6})D|uF$gU30Fe>=ZzIGI#`_tj*fyT3*po*akJ!FQK03}qv^&`Jgj<{2T5!9 zY*|vglrvqrCGa$f9g#}a{keDFaBFHPY;kTVY_@Tf^EJF(aCdK=e&k?!<3u3ge-oe2 zbhUZthwm7-5xy#m)0*Br6#>|7eP!NzIdO@D)PZ|F-go(%4g$A{B0#mwhXTB+a($iT zVa>UN?`O8+NT)AgkbWOtsWE`m{Mqj`%)9t8Z^oo5OuPuR`cgE(9XEJMX*39mO8JZe zZDAJ02N9v>kwYdSF%&2SxMb@k!L?Sv{Xh;Nt;QOUCKLx&8#e&eKwjOo4%D4XjaqQu z5*?>&ftL-Vgck#f!#8wWqLcuvk&EL5yx`0Y>D{r}_P^V^^Eg`H+}u~oA%6<&c}WHq zEEL|{bY7AVNH?7JyA3QpR1X55J)m+6La>;Zs0L)7o%=*T)2t$yvX=1JGtLN&SlzZU zb@POD^_VvEh_-}E@3n+5jF)&aJAzb2$js{@<}&|i6F^+KG=K%qO?#mQn;O@pCsrh) z*z>O@qfLcGkyS!$8yC}-eQe!JdXD0Q2hp@oIm%zzqC8Jo8NSlWQoxa@fdc55N z@J+ByEq%!@0#O3A++RI}(*SPyJ-jDwlR)y>1)R+J_p)|U%K&?#-4nX&5F~3BnP_cO zzVyHFG+`KNb5aPBKc>S~0(6C(AaKdR&3WeIQ(!?WwgAD$Moj{esj~y`HoqK{qZC2E zum(t~NczIj!G<_U-eNcB1qnouQrN5~Ap{&kr`;~{0u{Y?zKm3N90;CXe6(j{qU>L( zc2Xc^b^J}si1GZUW^Ud4>fwzc#s3Y!ZVOYJcX7}eK9CB* z#1EmRWHK5!5Rs8qCXuq$@5k2c8A1B5dAPQ7F|O0#QWVvcasp!y5eCB|z_TtD?SaQd zA%IFyRVfz(j1C&)e(VAp=f1H^nQGFS%z)LXV-^ok-Q(qKps_%)v(yXjA%%%4l)=w} zdgOcs8<2W0T=EVuw$ghi|B1Rs;1*J_0)@%<)B*D3^LB_8Fr$40SorQ#VUUvukc2O+ zb{7JFytwHa3OtIw7TKyK`}PGx6m(SDl7-@Z^U3%5ML~T%v&}%uQLJ1{O`u1LPj$<( zR-66hWP5~1_sY1~YYWX>F47M#*kFQd+Cj>;{%_Bqs+cZI$SgO@9s}eru%MG*B8(@K z#lK}lXRGm+ief{iNaXs|Z+&on!ML0ne)34{>oaV4)9x8B=peJ9pc)`Jxs0yS8+@z^ z9Fs?jt?i*ndsW(qs9Dv+k$yKvcq18y^i!t%EHUexZ-^OKuq)HeP~hB`dbxu36hUY? zIB-66`4l&l|G~CR|ChpX+!M0pjH?aQ8X{<&A7jx}n#OhN{0C1Mn$Kfb=p@WgcE#R5 zwES2!?$!ad4jG!b-iTM1I~8N_(_5cTDNWJ8Dio?dj;6kP+8_@WHV)xI(S{*u&$!7V zEN*kg3iK;pp#U@AAVcI)D-QRQpQZfTb?0_|U}q`^B1iy`!@mAaWj?@mWL2Q>Pm*5e zr*hzfKf9_8|66Ze3D;PGU8kOa{673nlRy_o42r*N)SeN)k?>{#nR{{>4~Ry4xpSLz z>)P<6DW_=|(!%+7P3~U?tpjcK!GZwb;P7 z$yo}EdbM?ySHEx~nWDXNJE5FYe%0U8<>G1nD?#w@Cu@F}`+U$O8?5!F8ZnFGsd3?! ztz-_d(ZYI5aV$|R(ba}CA&@S&^v7R@v>2qekUWo4S?2YdaC+BO|yX> z5^rtsHn!4R<D=5msK^e;VFMPYx%P!_B+qOU;8v9byyr5Wy=Exx$sPd-yDFK7k%?ylvfvB=sBcyZ7?W;do(?FE^drzmd=_9?La>FL{Q?B@~& zPC;xdVabLSJ*TGc3#~Ld_e*japsDvrQIza}@sDy$I!6)}NN-Ob8^CAIMs$&*_9Zgy z_Ga9P3g@_+Sz__NE=v3t#va(XcW3Hst6iSoVX~Xi=hX7*Fj^;2x=O!mW27a5=eRb$ znIZ%E^oNlSbRP6#*+8?uwO&;tBVrAqQ_uTJ4OBFye_G=mt~&AgQSwaoAIR%XVO&T% R?+5T72H5}r diff --git a/nym-wallet/src-tauri/scripts/appimage-wayland-hook.sh b/nym-wallet/src-tauri/scripts/appimage-wayland-hook.sh new file mode 100644 index 0000000000..2de2f123ef --- /dev/null +++ b/nym-wallet/src-tauri/scripts/appimage-wayland-hook.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +if [ -z "${WAYLAND_DISPLAY:-}" ]; then + return 0 2>/dev/null || exit 0 +fi + +if [ -z "${LD_PRELOAD:-}" ]; then + for lib_path in \ + /usr/lib/libwayland-client.so \ + /usr/lib64/libwayland-client.so \ + /usr/lib/x86_64-linux-gnu/libwayland-client.so + do + if [ -f "$lib_path" ]; then + export LD_PRELOAD="$lib_path" + break + fi + done +fi + +export GDK_BACKEND="${GDK_BACKEND:-wayland}" +export GDK_SCALE="${GDK_SCALE:-1}" +export GDK_DPI_SCALE="${GDK_DPI_SCALE:-0.8}" diff --git a/nym-wallet/src-tauri/src/error.rs b/nym-wallet/src-tauri/src/error.rs index 6f491d4532..414f2a7a92 100644 --- a/nym-wallet/src-tauri/src/error.rs +++ b/nym-wallet/src-tauri/src/error.rs @@ -14,7 +14,6 @@ use thiserror::Error; pub enum BackendError { #[error(transparent)] TypesError { - #[from] source: TypesError, }, #[error(transparent)] @@ -115,6 +114,8 @@ pub enum BackendError { WalletUnexpectedMnemonicAccount, #[error("Failed to derive address from mnemonic")] FailedToDeriveAddress, + #[error("Built-in HD derivation path constant failed to parse (internal error)")] + InvalidInternalDerivationPath, #[error(transparent)] ValueParseError(#[from] ParseIntError), #[error("The provided coin has an unknown denomination - {0}")] @@ -156,6 +157,10 @@ pub enum BackendError { #[error("there aren't any vesting delegations to migrate")] NoVestingDelegations, + /// Vesting contract [`nym_vesting_contract_common::VestingContractError::NoAccountForAddress`]. + #[error("Vesting contract has no account for this address")] + VestingContractAccountNotFound, + #[error("this command has been temporarily disabled")] Disabled, // @@ -172,8 +177,50 @@ impl Serialize for BackendError { } } +/// Cosmwasm returns vesting [`nym_vesting_contract_common::VestingContractError::NoAccountForAddress`] +/// as `VESTING (...): Account does not exist - ...` in the ABCI log. +fn nyxd_error_is_vesting_contract_no_account(err: &NyxdError) -> bool { + fn text_matches_strict(text: &str) -> bool { + text.contains("VESTING") && text.contains("Account does not exist") + } + // Prefer strict match; fall back for ABCI text that omits the `VESTING` prefix. Exclude Nyxd + // `NonExistentAccountError` (`... does not exist on the chain`). + fn abci_query_vesting_no_account(text: &str) -> bool { + text_matches_strict(text) + || (text.contains("Account does not exist") + && !text.contains("does not exist on the chain")) + } + match err { + NyxdError::AbciError { + log, + pretty_log, + .. + } => { + pretty_log + .as_ref() + .is_some_and(|s| abci_query_vesting_no_account(s)) + || abci_query_vesting_no_account(log) + } + _ => text_matches_strict(&err.to_string()), + } +} + +impl From for BackendError { + fn from(e: TypesError) -> Self { + if let TypesError::NyxdError { ref source } = e { + if nyxd_error_is_vesting_contract_no_account(source) { + return Self::VestingContractAccountNotFound; + } + } + Self::TypesError { source: e } + } +} + impl From for BackendError { fn from(source: NyxdError) -> Self { + if nyxd_error_is_vesting_contract_no_account(&source) { + return Self::VestingContractAccountNotFound; + } match source { NyxdError::AbciError { code: _, diff --git a/nym-wallet/src-tauri/src/log.rs b/nym-wallet/src-tauri/src/log.rs index d7642291e3..f9b61aaff2 100644 --- a/nym-wallet/src-tauri/src/log.rs +++ b/nym-wallet/src-tauri/src/log.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use fern::colors::{Color, ColoredLevelConfig}; use serde::Serialize; use serde_repr::{Deserialize_repr, Serialize_repr}; -use tauri::Emitter; +use tauri::{Emitter, Manager}; use time::{format_description, OffsetDateTime}; fn formatted_time() -> String { @@ -24,6 +24,7 @@ fn formatted_time() -> String { } pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerError> { + let log_window_app = app_handle.clone(); let colors = ColoredLevelConfig::new() .trace(Color::Magenta) .debug(Color::Blue) @@ -61,7 +62,10 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE message: record.args().to_string(), level: record.level().into(), }; - app_handle.emit("log://log", msg).unwrap(); + // Tauri 2: target the log webview explicitly so the dedicated window receives events. + if let Some(log_win) = log_window_app.get_webview_window("log") { + let _ = log_win.emit("log://log", msg); + } })); base_config diff --git a/nym-wallet/src-tauri/src/main.rs b/nym-wallet/src-tauri/src/main.rs index fe9aa8664c..de9c7f1070 100644 --- a/nym-wallet/src-tauri/src/main.rs +++ b/nym-wallet/src-tauri/src/main.rs @@ -4,9 +4,9 @@ )] use nym_mixnet_contract_common::{Gateway, MixNode}; -use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; use tauri::Manager; use tauri_plugin_opener::init as init_opener; +use tauri_plugin_process::init as init_process; use tauri_plugin_shell::init as init_shell; use tauri_plugin_updater::Builder as UpdaterBuilder; @@ -23,6 +23,7 @@ use crate::state::WalletState; mod config; mod error; mod log; +mod webview_theme; mod menu; mod network_config; mod operations; @@ -34,11 +35,13 @@ mod wallet_storage; #[allow(clippy::too_many_lines)] fn main() { dotenvy::dotenv().ok(); + configure_linux_wayland_defaults(); let context = tauri::generate_context!(); tauri::Builder::default() .plugin(init_shell()) .plugin(init_opener()) + .plugin(init_process()) .plugin(UpdaterBuilder::new().build()) .plugin(tauri_plugin_clipboard_manager::init()) .manage(WalletState::default()) @@ -217,25 +220,7 @@ fn main() { app::react::set_react_state, app::react::get_react_state, ]) - .menu(|app| { - // Create a menu builder - let menu_builder = MenuBuilder::new(app); - if ::std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() { - let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs") - .build(app) - .expect("Failed to create menu item"); - - let submenu = SubmenuBuilder::new(app, "Help") - .items(&[&help_text]) - .build() - .expect("Failed to create help submenu"); - - menu_builder.item(&submenu).build() - } else { - // Build a default menu without the submenu - menu_builder.build() - } - }) + .menu(|app| menu::build_app_menu(app)) .on_menu_event(|app, event| { if event.id() == SHOW_LOG_WINDOW { let _r = help::log::help_log_toggle_window(app.app_handle().clone()); @@ -245,3 +230,22 @@ fn main() { .run(context) .expect("error while running tauri application"); } + +fn configure_linux_wayland_defaults() { + #[cfg(target_os = "linux")] + { + if std::env::var_os("WAYLAND_DISPLAY").is_some() { + if std::env::var_os("GDK_BACKEND").is_none() { + unsafe { std::env::set_var("GDK_BACKEND", "wayland") }; + } + + if std::env::var_os("GDK_SCALE").is_none() { + unsafe { std::env::set_var("GDK_SCALE", "1") }; + } + + if std::env::var_os("GDK_DPI_SCALE").is_none() { + unsafe { std::env::set_var("GDK_DPI_SCALE", "0.8") }; + } + } + } +} diff --git a/nym-wallet/src-tauri/src/menu.rs b/nym-wallet/src-tauri/src/menu.rs index 58caa8b803..f762b75c2d 100644 --- a/nym-wallet/src-tauri/src/menu.rs +++ b/nym-wallet/src-tauri/src/menu.rs @@ -1,36 +1,25 @@ -use tauri::menu::Menu; -use tauri::menu::{MenuBuilder, MenuItemBuilder, SubmenuBuilder}; +use tauri::menu::{Menu, MenuBuilder, MenuItemBuilder, SubmenuBuilder}; +use tauri::{AppHandle, Runtime}; pub const SHOW_LOG_WINDOW: &str = "show_log_window"; -pub trait AddDefaultSubmenus { - #[allow(dead_code)] - fn add_default_app_submenus(self) -> Self; -} +pub fn build_app_menu(app: &AppHandle) -> tauri::Result> { + let edit_submenu = SubmenuBuilder::new(app, "Edit") + .cut() + .copy() + .paste() + .select_all() + .build()?; -impl AddDefaultSubmenus for Menu { - #[allow(dead_code)] - fn add_default_app_submenus(self) -> Self { - if ::std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() { - let app_handle = self.app_handle(); + let mut menu_builder = MenuBuilder::new(app).item(&edit_submenu); - let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs") - .build(app_handle) - .expect("Failed to create menu item"); + if std::env::var("NYM_WALLET_ENABLE_LOG").is_ok() { + let help_text = MenuItemBuilder::with_id(SHOW_LOG_WINDOW, "Show logs").build(app)?; - let submenu = SubmenuBuilder::new(app_handle, "Help") - .items(&[&help_text]) - .build() - .expect("Failed to create help submenu"); + let help_submenu = SubmenuBuilder::new(app, "Help").items(&[&help_text]).build()?; - let menu_builder = MenuBuilder::new(app_handle); - - match menu_builder.item(&submenu).build() { - Ok(new_menu) => new_menu, - Err(_) => self, - } - } else { - self - } + menu_builder = menu_builder.item(&help_submenu); } + + menu_builder.build() } diff --git a/nym-wallet/src-tauri/src/operations/app/link.rs b/nym-wallet/src-tauri/src/operations/app/link.rs index d238242633..d5e6602af3 100644 --- a/nym-wallet/src-tauri/src/operations/app/link.rs +++ b/nym-wallet/src-tauri/src/operations/app/link.rs @@ -1,8 +1,15 @@ use tauri_plugin_opener::OpenerExt; +use url::Url; #[tauri::command] pub async fn open_url(url: String, app_handle: tauri::AppHandle) -> Result<(), String> { - println!("Opening URL: {url}"); + let parsed = Url::parse(&url).map_err(|e| format!("Invalid URL: {e}"))?; + match parsed.scheme() { + "https" | "http" => {} + other => { + return Err(format!("URL scheme not allowed: {other}")); + } + } match app_handle.opener().open_url(&url, None::<&str>) { Ok(_) => Ok(()), diff --git a/nym-wallet/src-tauri/src/operations/app/version.rs b/nym-wallet/src-tauri/src/operations/app/version.rs index d69a80f5e3..e36509ac50 100644 --- a/nym-wallet/src-tauri/src/operations/app/version.rs +++ b/nym-wallet/src-tauri/src/operations/app/version.rs @@ -15,10 +15,29 @@ pub async fn check_version(handle: tauri::AppHandle) -> Result info, + Err(e) => { + let msg = e.to_string(); + // Hosted static JSON must include per-platform `signature` (base64) alongside `url`. + // Legacy manifests with only `url` fail serde in tauri-plugin-updater 2.x. + if msg.contains("missing field") && msg.contains("signature") { + let current_version = handle.package_info().version.to_string(); + log::warn!( + "Updater check skipped: manifest at configured endpoint is not Tauri 2-compatible \ + (missing or invalid `signature` field). Users will not be notified of updates \ + until the hosted updater.json is republished. Error: {msg}" + ); + return Ok(AppVersion { + current_version: current_version.clone(), + latest_version: current_version, + is_update_available: false, + }); + } + log::error!("An error occurred while checking for app update {e}"); + return Err(BackendError::CheckAppVersionError); + } + }; // Process the result if let Some(update) = update_info { diff --git a/nym-wallet/src-tauri/src/operations/app/window.rs b/nym-wallet/src-tauri/src/operations/app/window.rs index cb60f8192b..1d5482e409 100644 --- a/nym-wallet/src-tauri/src/operations/app/window.rs +++ b/nym-wallet/src-tauri/src/operations/app/window.rs @@ -1,6 +1,7 @@ use tauri::Manager; use crate::error::BackendError; +use crate::webview_theme::NYM_WALLET_WEBVIEW_BG; #[tauri::command] pub async fn create_main_window(app_handle: tauri::AppHandle) -> Result<(), BackendError> { @@ -32,6 +33,7 @@ async fn create_window( tauri::WebviewUrl::App(new_window_url.into()), ) .title("Nym Wallet") + .background_color(NYM_WALLET_WEBVIEW_BG) .build() { Ok(window) => { diff --git a/nym-wallet/src-tauri/src/operations/help/log.rs b/nym-wallet/src-tauri/src/operations/help/log.rs index 42f15fb02f..08a90a0085 100644 --- a/nym-wallet/src-tauri/src/operations/help/log.rs +++ b/nym-wallet/src-tauri/src/operations/help/log.rs @@ -1,4 +1,5 @@ use crate::error::BackendError; +use crate::webview_theme::NYM_WALLET_WEBVIEW_BG; use tauri::Manager; #[tauri::command] @@ -18,6 +19,7 @@ pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), Backen tauri::WebviewUrl::App("log.html".into()), ) .title("Nym Wallet Logs") + .background_color(NYM_WALLET_WEBVIEW_BG) .build() { Ok(window) => { diff --git a/nym-wallet/src-tauri/src/operations/mixnet/account.rs b/nym-wallet/src-tauri/src/operations/mixnet/account.rs index 63dc75e884..bc8d1e4c1f 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/account.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/account.rs @@ -18,6 +18,12 @@ use std::collections::HashMap; use strum::IntoEnumIterator; use url::Url; +fn cosmos_derivation_path() -> Result { + COSMOS_DERIVATION_PATH + .parse() + .map_err(|_| BackendError::InvalidInternalDerivationPath) +} + #[tauri::command] pub async fn connect_with_mnemonic( mnemonic: Mnemonic, @@ -48,8 +54,9 @@ pub async fn get_balance(state: tauri::State<'_, WalletState>) -> Result Mnemonic { - random_mnemonic() +pub fn create_new_mnemonic() -> Result { + let mut rng = rand::thread_rng(); + Mnemonic::generate_in_with(&mut rng, Language::English, 24).map_err(Into::into) } #[tauri::command] @@ -82,11 +89,6 @@ pub async fn logout(state: tauri::State<'_, WalletState>) -> Result<(), BackendE Ok(()) } -fn random_mnemonic() -> Mnemonic { - let mut rng = rand::thread_rng(); - Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap() -} - async fn _connect_with_mnemonic( mnemonic: Mnemonic, state: tauri::State<'_, WalletState>, @@ -129,12 +131,26 @@ async fn _connect_with_mnemonic( let default_nyxd_urls: HashMap = untested_nyxd_urls .iter() - .map(|(network, urls)| (*network, urls.iter().next().unwrap().clone())) - .collect(); + .map(|(network, urls)| { + let url = urls + .iter() + .next() + .cloned() + .ok_or(BackendError::WalletNoDefaultValidator)?; + Ok((*network, url)) + }) + .collect::, BackendError>>()?; let default_api_urls: HashMap = untested_api_urls .iter() - .map(|(network, urls)| (*network, urls.iter().next().unwrap().clone())) - .collect(); + .map(|(network, urls)| { + let url = urls + .iter() + .next() + .cloned() + .ok_or(BackendError::WalletNoDefaultValidator)?; + Ok((*network, url)) + }) + .collect::, BackendError>>()?; let nyxd_urls = pick_good_nyxd_urls(&default_nyxd_urls, &nyxd_urls).await?; let api_urls = pick_good_api_urls(&default_api_urls, &api_urls).await?; @@ -339,7 +355,7 @@ pub fn create_password(mnemonic: Mnemonic, password: UserPassword) -> Result<(), } log::info!("Creating password"); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let hd_path: DerivationPath = cosmos_derivation_path()?; // Currently we only support a single, default, login id in the wallet let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); wallet_storage::store_login_with_multiple_accounts(mnemonic, hd_path, login_id, &password) @@ -451,7 +467,7 @@ pub async fn add_account_for_password( state: tauri::State<'_, WalletState>, ) -> Result { log::info!("Adding account for the current password: {account_id}"); - let hd_path: DerivationPath = COSMOS_DERIVATION_PATH.parse().unwrap(); + let hd_path: DerivationPath = cosmos_derivation_path()?; // Currently we only support a single, default, login id in the wallet let login_id = wallet_storage::LoginId::new(DEFAULT_LOGIN_ID.to_string()); let account_id = wallet_storage::AccountId::new(account_id.to_string()); @@ -501,27 +517,24 @@ async fn set_state_with_all_accounts( let all_account_ids: Vec = all_accounts .iter() - .map(|account| { + .map(|account| -> Result { let mnemonic = account.mnemonic(); let addresses: HashMap = WalletNetwork::iter() .map(|network| { let config_network: NymNetworkDetails = network.into(); - ( - network, - derive_address( - mnemonic.clone(), - &config_network.chain_details.bech32_account_prefix, - ) - .unwrap(), - ) + let addr = derive_address( + mnemonic.clone(), + &config_network.chain_details.bech32_account_prefix, + )?; + Ok((network, addr)) }) - .collect(); - WalletAccountIds { + .collect::, BackendError>>()?; + Ok(WalletAccountIds { id: account.id().clone(), addresses, - } + }) }) - .collect(); + .collect::, BackendError>>()?; let mut w_state = state.write().await; w_state.set_all_accounts(all_account_ids); diff --git a/nym-wallet/src-tauri/src/operations/vesting/queries.rs b/nym-wallet/src-tauri/src/operations/vesting/queries.rs index 750d03caee..6cf027fec2 100644 --- a/nym-wallet/src-tauri/src/operations/vesting/queries.rs +++ b/nym-wallet/src-tauri/src/operations/vesting/queries.rs @@ -16,7 +16,7 @@ pub(crate) async fn locked_coins( block_time: Option, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query locked coins"); + log::debug!(">>> Query locked coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -28,7 +28,7 @@ pub(crate) async fn locked_coins( ) .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< locked coins = {display}"); + log::debug!("<<< locked coins = {display}"); Ok(display) } @@ -37,7 +37,7 @@ pub(crate) async fn spendable_coins( block_time: Option, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query spendable coins"); + log::debug!(">>> Query spendable coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -50,7 +50,7 @@ pub(crate) async fn spendable_coins( .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< spendable coins = {display}"); + log::debug!("<<< spendable coins = {display}"); Ok(display) } @@ -58,7 +58,7 @@ pub(crate) async fn spendable_coins( pub(crate) async fn spendable_vested_coins( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query spendable vested coins"); + log::debug!(">>> Query spendable vested coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -68,7 +68,7 @@ pub(crate) async fn spendable_vested_coins( .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< spendable vested coins = {display}"); + log::debug!("<<< spendable vested coins = {display}"); Ok(display) } @@ -76,7 +76,7 @@ pub(crate) async fn spendable_vested_coins( pub(crate) async fn spendable_reward_coins( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query spendable reward coins"); + log::debug!(">>> Query spendable reward coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -86,7 +86,7 @@ pub(crate) async fn spendable_reward_coins( .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< spendable reward coins = {display}"); + log::debug!("<<< spendable reward coins = {display}"); Ok(display) } @@ -96,7 +96,7 @@ pub(crate) async fn vested_coins( block_time: Option, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query vested coins"); + log::debug!(">>> Query vested coins"); let guard = state.read().await; let res = guard @@ -109,7 +109,7 @@ pub(crate) async fn vested_coins( .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< vested coins = {display}"); + log::debug!("<<< vested coins = {display}"); Ok(display) } @@ -119,7 +119,7 @@ pub(crate) async fn vesting_coins( block_time: Option, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query vesting coins"); + log::debug!(">>> Query vesting coins"); let guard = state.read().await; let res = guard @@ -132,7 +132,7 @@ pub(crate) async fn vesting_coins( .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< vesting coins = {display}"); + log::debug!("<<< vesting coins = {display}"); Ok(display) } @@ -141,12 +141,12 @@ pub(crate) async fn vesting_start_time( vesting_account_address: &str, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query vesting start time"); + log::debug!(">>> Query vesting start time"); let res = nyxd_client!(state) .vesting_start_time(vesting_account_address) .await? .seconds(); - log::info!("<<< vesting start time = {res}"); + log::debug!("<<< vesting start time = {res}"); Ok(res) } @@ -155,12 +155,12 @@ pub(crate) async fn vesting_end_time( vesting_account_address: &str, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query vesting end time"); + log::debug!(">>> Query vesting end time"); let res = nyxd_client!(state) .vesting_end_time(vesting_account_address) .await? .seconds(); - log::info!("<<< vesting end time = {res}"); + log::debug!("<<< vesting end time = {res}"); Ok(res) } @@ -169,7 +169,7 @@ pub(crate) async fn original_vesting( vesting_account_address: &str, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query original vesting"); + log::debug!(">>> Query original vesting"); let guard = state.read().await; let reg = guard.registered_coins()?; @@ -180,7 +180,7 @@ pub(crate) async fn original_vesting( .await?; let res = OriginalVestingResponse::from_vesting_contract(res, reg)?; - log::info!("<<< {res:?}"); + log::debug!("<<< {res:?}"); Ok(res) } @@ -188,7 +188,7 @@ pub(crate) async fn original_vesting( pub(crate) async fn get_historical_vesting_staking_reward( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query historical vesting staking reward coins"); + log::debug!(">>> Query historical vesting staking reward coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -197,7 +197,7 @@ pub(crate) async fn get_historical_vesting_staking_reward( .get_historical_vesting_staking_reward(client.nyxd.address().as_ref()) .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< historical vesting staking reward coins = {display}"); + log::debug!("<<< historical vesting staking reward coins = {display}"); Ok(display) } @@ -205,7 +205,7 @@ pub(crate) async fn get_historical_vesting_staking_reward( pub(crate) async fn get_spendable_vested_coins( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query spendable vested coins"); + log::debug!(">>> Query spendable vested coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -214,7 +214,7 @@ pub(crate) async fn get_spendable_vested_coins( .get_spendable_vested_coins(client.nyxd.address().as_ref()) .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< spendable vested coins = {display}"); + log::debug!("<<< spendable vested coins = {display}"); Ok(display) } @@ -222,7 +222,7 @@ pub(crate) async fn get_spendable_vested_coins( pub(crate) async fn get_spendable_reward_coins( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query spendable reward coins"); + log::debug!(">>> Query spendable reward coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -231,7 +231,7 @@ pub(crate) async fn get_spendable_reward_coins( .get_spendable_reward_coins(client.nyxd.address().as_ref()) .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< spendable reward coins = {display}"); + log::debug!("<<< spendable reward coins = {display}"); Ok(display) } @@ -239,7 +239,7 @@ pub(crate) async fn get_spendable_reward_coins( pub(crate) async fn get_delegated_coins( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query delegated coins"); + log::debug!(">>> Query delegated coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -248,7 +248,7 @@ pub(crate) async fn get_delegated_coins( .get_delegated_coins(client.nyxd.address().as_ref()) .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< delegated coins = {display}"); + log::debug!("<<< delegated coins = {display}"); Ok(display) } @@ -256,7 +256,7 @@ pub(crate) async fn get_delegated_coins( pub(crate) async fn get_pledged_coins( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query pledged coins"); + log::debug!(">>> Query pledged coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -265,7 +265,7 @@ pub(crate) async fn get_pledged_coins( .get_pledged_coins(client.nyxd.address().as_ref()) .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< pledged coins = {display}"); + log::debug!("<<< pledged coins = {display}"); Ok(display) } @@ -273,7 +273,7 @@ pub(crate) async fn get_pledged_coins( pub(crate) async fn get_staked_coins( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query staked coins"); + log::debug!(">>> Query staked coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -282,7 +282,7 @@ pub(crate) async fn get_staked_coins( .get_staked_coins(client.nyxd.address().as_ref()) .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< staked coins = {display}"); + log::debug!("<<< staked coins = {display}"); Ok(display) } @@ -290,7 +290,7 @@ pub(crate) async fn get_staked_coins( pub(crate) async fn get_withdrawn_coins( state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query withdrawn coins"); + log::debug!(">>> Query withdrawn coins"); let guard = state.read().await; let client = guard.current_client()?; @@ -299,7 +299,7 @@ pub(crate) async fn get_withdrawn_coins( .get_withdrawn_coins(client.nyxd.address().as_ref()) .await?; let display = guard.attempt_convert_to_display_dec_coin(res)?; - log::info!("<<< pledged coins = {display}"); + log::debug!("<<< withdrawn coins = {display}"); Ok(display) } @@ -309,7 +309,7 @@ pub(crate) async fn delegated_free( _block_time: Option, _state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query delegated free -> THIS QUERY HAS BEEN REMOVED FROM THE CONTRACT"); + log::warn!(">>> Query delegated free -> THIS QUERY HAS BEEN REMOVED FROM THE CONTRACT"); Err(BackendError::RemovedCommand { name: "vesting::queries::delegated_free".to_string(), alternative: "vesting::queries::get_delegated_coins".to_string(), @@ -323,7 +323,7 @@ pub(crate) async fn delegated_vesting( _vesting_account_address: &str, _state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query delegated vesting -> THIS QUERY HAS BEEN REMOVED FROM THE CONTRACT"); + log::warn!(">>> Query delegated vesting -> THIS QUERY HAS BEEN REMOVED FROM THE CONTRACT"); Err(BackendError::RemovedCommand { name: "vesting::queries::delegated_vesting".to_string(), alternative: "vesting::queries::get_delegated_coins".to_string(), @@ -335,7 +335,7 @@ pub(crate) async fn vesting_get_mixnode_pledge( address: &str, state: tauri::State<'_, WalletState>, ) -> Result, BackendError> { - log::info!(">>> Query vesting get mixnode pledge"); + log::debug!(">>> Query vesting get mixnode pledge"); let guard = state.read().await; let reg = guard.registered_coins()?; @@ -347,7 +347,7 @@ pub(crate) async fn vesting_get_mixnode_pledge( .map(|pledge| PledgeData::from_vesting_contract(pledge, reg)) .transpose()?; - log::info!("<<< {res:?}"); + log::debug!("<<< {res:?}"); Ok(res) } @@ -356,7 +356,7 @@ pub(crate) async fn vesting_get_gateway_pledge( address: &str, state: tauri::State<'_, WalletState>, ) -> Result, BackendError> { - log::info!(">>> Query vesting get gateway pledge"); + log::debug!(">>> Query vesting get gateway pledge"); let guard = state.read().await; let reg = guard.registered_coins()?; @@ -368,7 +368,7 @@ pub(crate) async fn vesting_get_gateway_pledge( .map(|pledge| PledgeData::from_vesting_contract(pledge, reg)) .transpose()?; - log::info!("<<< {res:?}"); + log::debug!("<<< {res:?}"); Ok(res) } @@ -377,11 +377,11 @@ pub(crate) async fn get_current_vesting_period( address: &str, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query current vesting period"); + log::debug!(">>> Query current vesting period"); let res = nyxd_client!(state) .get_current_vesting_period(address) .await?; - log::info!("<<< {res:?}"); + log::debug!("<<< {res:?}"); Ok(res) } @@ -390,13 +390,13 @@ pub(crate) async fn get_account_info( address: &str, state: tauri::State<'_, WalletState>, ) -> Result { - log::info!(">>> Query account info"); + log::debug!(">>> Query account info"); let guard = state.read().await; let res = guard.registered_coins()?; let vesting_account = guard.current_client()?.nyxd.get_account(address).await?; let res = VestingAccountInfo::from_vesting_contract(vesting_account, res)?; - log::info!("<<< {res:?}"); + log::debug!("<<< {res:?}"); Ok(res) } diff --git a/nym-wallet/src-tauri/src/webview_theme.rs b/nym-wallet/src-tauri/src/webview_theme.rs new file mode 100644 index 0000000000..5487163379 --- /dev/null +++ b/nym-wallet/src-tauri/src/webview_theme.rs @@ -0,0 +1,5 @@ +//! Shared webview chrome (matches wallet dark `background.default` #242B2D). + +use tauri::window::Color; + +pub const NYM_WALLET_WEBVIEW_BG: Color = Color(36, 43, 45, 255); diff --git a/nym-wallet/src-tauri/tauri.conf.json b/nym-wallet/src-tauri/tauri.conf.json index 75193606bf..b760597d1b 100644 --- a/nym-wallet/src-tauri/tauri.conf.json +++ b/nym-wallet/src-tauri/tauri.conf.json @@ -26,6 +26,11 @@ "entitlements": null }, "linux": { + "appimage": { + "files": { + "/apprun-hooks/99-nym-wayland.sh": "scripts/appimage-wayland-hook.sh" + } + }, "deb": { "depends": [] } @@ -55,15 +60,30 @@ "capabilities": [ "main-capability" ], - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'; connect-src ipc: http://ipc.localhost" + "csp": { + "default-src": "'self' customprotocol: asset:", + "script-src": "'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'", + "style-src": "'unsafe-inline' 'self'", + "img-src": "'self' asset: http://asset.localhost https://asset.localhost blob: data:", + "font-src": "'self' data:", + "connect-src": "ipc: http://ipc.localhost http://127.0.0.1:* http://localhost:* ws://127.0.0.1:* ws://localhost:* wss://127.0.0.1:* wss://localhost:* http: https: ws: wss:", + "media-src": "'self' blob: data:", + "worker-src": "'self' blob:", + "frame-src": "'none'", + "object-src": "'none'", + "base-uri": "'self'" + } }, "windows": [ { "title": "Nym Wallet", "width": 1268, "height": 768, + "minWidth": 1024, + "minHeight": 640, "resizable": true, - "useHttpsScheme": true + "useHttpsScheme": true, + "backgroundColor": "#242b2d" } ] } diff --git a/nym-wallet/src/api/networkOverview.test.ts b/nym-wallet/src/api/networkOverview.test.ts new file mode 100644 index 0000000000..33f0c08a9d --- /dev/null +++ b/nym-wallet/src/api/networkOverview.test.ts @@ -0,0 +1,51 @@ +import { fetchNymPriceDeduped } from './networkOverview'; + +const sampleTokenomics = { + quotes: { + USD: { + price: 0.0331, + market_cap: 26_000_000, + volume_24h: 1_200_000, + }, + }, +}; + +describe('fetchNymPriceDeduped', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('coalesces concurrent requests for the same URL', async () => { + let callCount = 0; + global.fetch = jest.fn(() => { + callCount += 1; + return Promise.resolve({ + ok: true, + json: () => Promise.resolve(sampleTokenomics), + } as Response); + }); + + const url = 'https://api.example.test/v1/nym-price'; + const p1 = fetchNymPriceDeduped(url); + const p2 = fetchNymPriceDeduped(url); + const [a, b] = await Promise.all([p1, p2]); + + expect(a).toStrictEqual(sampleTokenomics); + expect(b).toStrictEqual(sampleTokenomics); + expect(callCount).toBe(1); + }); + + it('does not coalesce different URLs', async () => { + let callCount = 0; + global.fetch = jest.fn(() => { + callCount += 1; + return Promise.resolve({ + ok: true, + json: () => Promise.resolve(sampleTokenomics), + } as Response); + }); + + await Promise.all([fetchNymPriceDeduped('https://a.test/p'), fetchNymPriceDeduped('https://b.test/p')]); + expect(callCount).toBe(2); + }); +}); diff --git a/nym-wallet/src/api/networkOverview.ts b/nym-wallet/src/api/networkOverview.ts new file mode 100644 index 0000000000..40c4ba1725 --- /dev/null +++ b/nym-wallet/src/api/networkOverview.ts @@ -0,0 +1,175 @@ +import { addSeconds } from 'date-fns'; +import type { Network } from 'src/types'; + +export type NetworkOverviewEndpoints = { + mixnodeStats: string; + epochCurrent: string; + epochRewards: string; + nymPrice: string; + observatoryNodesBase: string; +}; + +export const getNetworkOverviewEndpoints = (network?: Network): NetworkOverviewEndpoints => { + if (network === 'SANDBOX' || network === 'QA') { + return { + mixnodeStats: 'https://sandbox-node-status-api.nymte.ch/v2/mixnodes/stats', + epochCurrent: 'https://sandbox-nym-api1.nymtech.net/api/v1/epoch/current', + epochRewards: 'https://sandbox-nym-api1.nymtech.net/api/v1/epoch/reward_params', + nymPrice: 'https://api.nym.spectredao.net/api/v1/nym-price', + observatoryNodesBase: 'https://sandbox-node-status-api.nymte.ch/explorer/v3/nym-nodes', + }; + } + return { + mixnodeStats: 'https://mainnet-node-status-api.nymtech.cc/v2/mixnodes/stats', + epochCurrent: 'https://validator.nymtech.net/api/v1/epoch/current', + epochRewards: 'https://validator.nymtech.net/api/v1/epoch/reward_params', + nymPrice: 'https://api.nym.spectredao.net/api/v1/nym-price', + observatoryNodesBase: 'https://mainnet-node-status-api.nymtech.cc/explorer/v3/nym-nodes', + }; +}; + +export interface PacketsAndStakingPoint { + date_utc: string; + total_packets_received: number; + total_packets_sent: number; + total_packets_dropped: number; + total_stake: number; +} + +export interface CurrentEpochApiData { + id: number; + current_epoch_id: number; + current_epoch_start: string; + epoch_length: { secs: number; nanos: number }; + epochs_in_interval: number; + total_elapsed_epochs: number; +} + +export type CurrentEpochWithEnd = CurrentEpochApiData & { current_epoch_end: string }; + +export interface EpochRewardsData { + interval: { + reward_pool: string; + staking_supply: string; + staking_supply_scale_factor: string; + epoch_reward_budget: string; + stake_saturation_point: string; + active_set_work_factor: string; + interval_pool_emission: string; + sybil_resistance: string; + }; + rewarded_set: { + entry_gateways: number; + exit_gateways: number; + mixnodes: number; + standby: number; + }; +} + +export interface NymTokenomics { + quotes: { + USD: { + price: number; + market_cap: number; + volume_24h: number; + }; + }; +} + +type ObservatoryPage = { + page: number; + size: number; + total: number; + items: Array<{ rewarding_details?: { unique_delegations?: number } }>; +}; + +const jsonHeaders = { + Accept: 'application/json', + 'Content-Type': 'application/json; charset=utf-8', +}; + +export async function fetchPacketsAndStaking(url: string): Promise { + const response = await fetch(url, { headers: jsonHeaders }); + if (!response.ok) { + throw new Error('Failed to fetch mixnode stats'); + } + const data: PacketsAndStakingPoint[] = await response.json(); + return data; +} + +export async function fetchCurrentEpoch(url: string): Promise { + const response = await fetch(url, { headers: jsonHeaders, cache: 'no-store' }); + if (!response.ok) { + throw new Error('Failed to fetch current epoch'); + } + const data: CurrentEpochApiData = await response.json(); + const current_epoch_end = addSeconds(new Date(data.current_epoch_start), data.epoch_length.secs).toISOString(); + return { ...data, current_epoch_end }; +} + +export async function fetchEpochRewards(url: string): Promise { + const response = await fetch(url, { headers: jsonHeaders, cache: 'no-store' }); + if (!response.ok) { + throw new Error('Failed to fetch epoch rewards'); + } + return response.json(); +} + +export async function fetchNymPrice(url: string): Promise { + const response = await fetch(url, { headers: jsonHeaders }); + if (!response.ok) { + throw new Error('Failed to fetch NYM price'); + } + return response.json(); +} + +const nymPriceInflight = new Map>(); + +/** Coalesces concurrent requests for the same price URL (e.g. Balance card + Network overview). */ +export function fetchNymPriceDeduped(url: string): Promise { + const existing = nymPriceInflight.get(url); + if (existing) { + return existing; + } + const pending = fetchNymPrice(url).finally(() => { + nymPriceInflight.delete(url); + }); + nymPriceInflight.set(url, pending); + return pending; +} + +/** Paginates observatory nodes and sums unique_delegations (same idea as explorer StakersNumberCard). */ +export async function fetchTotalDelegationsCount(baseUrl: string): Promise { + const all: ObservatoryPage['items'] = []; + let page = 0; + const PAGE_SIZE = 200; + let hasMore = true; + + while (hasMore) { + /* Paginated API: each request needs the previous page index. */ + // eslint-disable-next-line no-await-in-loop -- sequential pagination + const response = await fetch(`${baseUrl}?page=${page}&size=${PAGE_SIZE}`, { headers: jsonHeaders }); + if (!response.ok) { + throw new Error(`Failed to fetch observatory nodes (page ${page})`); + } + // eslint-disable-next-line no-await-in-loop -- follows fetch above + const data: ObservatoryPage = await response.json(); + all.push(...data.items); + const totalPages = Math.ceil(data.total / PAGE_SIZE); + if (page >= totalPages - 1 || data.items.length < PAGE_SIZE) { + hasMore = false; + } else { + page += 1; + } + } + + return all.reduce((sum, node) => sum + (node.rewarding_details?.unique_delegations ?? 0), 0); +} + +export function formatCompactNumber(num: number): string { + return new Intl.NumberFormat('en-US', { + maximumFractionDigits: 2, + notation: 'compact', + compactDisplay: 'short', + }).format(num); +} diff --git a/nym-wallet/src/common.tsx b/nym-wallet/src/common.tsx index 2a40368d31..afd4d8e57a 100644 --- a/nym-wallet/src/common.tsx +++ b/nym-wallet/src/common.tsx @@ -7,6 +7,12 @@ import { ErrorFallback } from './components'; import { NymWalletTheme } from './theme'; import { maximizeWindow } from './utils'; import { config } from './config'; +import { useTauriTextEditingClipboard } from './hooks/useTauriTextEditingClipboard'; + +const ClipboardBridge: FCWithChildren = ({ children }) => { + useTauriTextEditingClipboard(); + return children; +}; export const AppCommon = ({ children }: { children: React.ReactNode }) => { useEffect(() => { @@ -26,7 +32,9 @@ export const AppCommon = ({ children }: { children: React.ReactNode }) => { }} > - {children} + + {children} + diff --git a/nym-wallet/src/components/Accounts/AccountOverview.tsx b/nym-wallet/src/components/Accounts/AccountOverview.tsx index 95c3245ae3..744d3569fe 100644 --- a/nym-wallet/src/components/Accounts/AccountOverview.tsx +++ b/nym-wallet/src/components/Accounts/AccountOverview.tsx @@ -2,11 +2,12 @@ import React from 'react'; import { Button } from '@mui/material'; import { AccountEntry } from '@nymproject/types'; import { AccountAvatar } from './AccountAvatar'; +import { headerControlPillSx } from '../headerControlPillSx'; export const AccountOverview = ({ account, onClick }: { account: AccountEntry; onClick: () => void }) => ( - + Bonding locks NYM as pledge so you can run a nym node on the network. You will need enough liquid NYM to cover + the minimum pledge and fees. Read the{' '} + + node setup and bonding guide + {' '} + before you continue. + + ); diff --git a/nym-wallet/src/components/Bonding/modals/NodeSettingsModal.tsx b/nym-wallet/src/components/Bonding/modals/NodeSettingsModal.tsx index 8d87405d13..e1baf9c43d 100644 --- a/nym-wallet/src/components/Bonding/modals/NodeSettingsModal.tsx +++ b/nym-wallet/src/components/Bonding/modals/NodeSettingsModal.tsx @@ -95,6 +95,8 @@ export const NodeSettings = ({ return ( Your new profit margin will be applied in the next interval - + @@ -73,6 +83,22 @@ const ExchangeCard = ({ ); export const Tutorial = () => { + const theme = useTheme(); + const { enqueueSnackbar } = useSnackbar(); + + const openExchange = async (name: string, url: string) => { + try { + enqueueSnackbar(`Opening ${name} in your default browser - always verify the URL in the address bar.`, { + variant: 'info', + }); + await safeOpenUrl(url); + } catch (e) { + enqueueSnackbar('Could not open the link. Copy the URL from the exchange website instead.', { + variant: 'error', + }); + } + }; + const exchanges = [ { name: 'Bitfinex', @@ -107,7 +133,15 @@ export const Tutorial = () => { ]; return ( - + You can get NYM tokens from these exchanges @@ -115,7 +149,7 @@ export const Tutorial = () => { {exchanges.map((exchange) => ( - + ))} diff --git a/nym-wallet/src/components/Clipboard/ClipboardActions.tsx b/nym-wallet/src/components/Clipboard/ClipboardActions.tsx index 26af84a25e..dbb8185078 100644 --- a/nym-wallet/src/components/Clipboard/ClipboardActions.tsx +++ b/nym-wallet/src/components/Clipboard/ClipboardActions.tsx @@ -71,6 +71,7 @@ export const ClipboardActions = ({ if (!fieldRef) return; const keydownHandler = async (e: KeyboardEvent) => { + if (e.defaultPrevented) return; // Only handle if the associated field is focused const { activeElement } = document; if (fieldRef.current && activeElement === fieldRef.current) { diff --git a/nym-wallet/src/components/Clipboard/ClipboardFormFields.tsx b/nym-wallet/src/components/Clipboard/ClipboardFormFields.tsx index 370fba1806..e30cf4fd69 100644 --- a/nym-wallet/src/components/Clipboard/ClipboardFormFields.tsx +++ b/nym-wallet/src/components/Clipboard/ClipboardFormFields.tsx @@ -6,6 +6,11 @@ import { UseFormRegister, UseFormSetValue, FieldValues, Path, FieldErrors } from import { writeText, readText } from '@tauri-apps/plugin-clipboard-manager'; import { PasteFromClipboard } from './ClipboardActions'; +/** + * Keyboard clipboard helpers for fields that opt in via this module. + * App-wide Tauri paste/copy for normal inputs lives in `useTauriTextEditingClipboard` (see + * `data-nym-paste-replace`, `data-nym-currency-field`, `data-nym-auth-paste-field` exclusions). + */ export const useCopyAllSupport = ( inputRef: React.MutableRefObject, onPasteValue?: (value: string) => void, @@ -15,6 +20,7 @@ export const useCopyAllSupport = ( const handleKeyDown = async (e: Event) => { const keyEvent = e as KeyboardEvent; + if (keyEvent.defaultPrevented) return; if (document.activeElement !== inputRef.current) return; @@ -71,46 +77,47 @@ export const useCopyAllSupport = ( }, [inputRef.current, onPasteValue]); }; -export const TextFieldWithPaste = React.forwardRef< - HTMLDivElement, - TextFieldProps & { - onPasteValue?: (value: string) => void; - } ->(({ onPasteValue, ...props }, ref) => { - const inputRef = useRef(null); +export type TextFieldWithPasteProps = TextFieldProps & { + // eslint-disable-next-line react/require-default-props -- optional on `forwardRef` + intersection props; see `onPasteValue = undefined` in render + onPasteValue?: (value: string) => void; +}; - useCopyAllSupport(inputRef, onPasteValue); +export const TextFieldWithPaste = React.forwardRef( + ({ onPasteValue = undefined, inputProps: userInputProps, InputProps: userInputPropsMui, ...props }, ref) => { + const inputRef = useRef(null); - const handlePaste = (pastedText: string) => { - onPasteValue?.(pastedText); + useCopyAllSupport(inputRef, onPasteValue); - if (inputRef.current) { - inputRef.current.focus(); - } - }; + const handlePaste = (pastedText: string) => { + onPasteValue?.(pastedText); - return ( - {onPasteValue && } - {props.InputProps?.endAdornment} + {userInputPropsMui?.endAdornment} ), - }} - /> - ); -}); + }, + }; -// Add defaultProps to fix the "require-default-props" warning -TextFieldWithPaste.defaultProps = { - onPasteValue: undefined, -}; + return ; + }, +); export const CurrencyFormFieldWithPaste = ({ label, @@ -167,6 +174,7 @@ export const CurrencyFormFieldWithPaste = ({ const handleKeyDown = async (e: Event) => { const keyEvent = e as KeyboardEvent; + if (keyEvent.defaultPrevented) return; if (document.activeElement !== inputRef.current) return; // Handle Cmd+A (Select All) @@ -236,7 +244,7 @@ export const CurrencyFormFieldWithPaste = ({ }, []); return ( - + ({ register, setValue, errors, + inputProps: userInputProps, + InputProps: userInputPropsMui, ...props }: { name: Path; @@ -295,25 +305,29 @@ export const HookFormTextFieldWithPaste = ({ // Pass handlePaste to useCopyAllSupport for Cmd+V handling useCopyAllSupport(inputRef, handlePaste); - return ( - - - - ), - ...props.InputProps, - }} - {...props} - /> - ); + const fieldProps = { + ...register(name), + ...props, + label, + error: Boolean(errors[name]), + helperText: errors[name]?.message?.toString(), + inputRef, + inputProps: { + ...userInputProps, + 'data-nym-paste-replace': 'true', + }, + InputProps: { + ...userInputPropsMui, + endAdornment: ( + + + {userInputPropsMui?.endAdornment} + + ), + }, + }; + + return ; }; export const HookFormCurrencyFieldWithPaste = ({ @@ -361,6 +375,7 @@ export const HookFormCurrencyFieldWithPaste = const handleKeyDown = async (e: Event) => { const keyEvent = e as KeyboardEvent; + if (keyEvent.defaultPrevented) return; if (document.activeElement !== inputRef.current) return; // Handle Cmd+A (Select All) @@ -435,7 +450,7 @@ export const HookFormCurrencyFieldWithPaste = }; return ( - + ; - -const Template: ComponentStory = (args) => ( - - - - - -); - -export const Default = Template.bind({}); -Default.args = { - open: true, - header: 'Confirm transaction', - subheader: 'Confirm and proceed or cancel transaction', - fee: { amount: { amount: '0.001', denom: 'nym' }, fee: { Auto: null } }, - onClose: () => {}, - onConfirm: async () => {}, - onPrev: () => {}, - isStorybook: true, -}; diff --git a/nym-wallet/src/components/ConfirmTX.tsx b/nym-wallet/src/components/ConfirmTX.tsx index 2e3d386f83..a9073cae27 100644 --- a/nym-wallet/src/components/ConfirmTX.tsx +++ b/nym-wallet/src/components/ConfirmTX.tsx @@ -1,19 +1,9 @@ import React from 'react'; import { FeeDetails } from '@nymproject/types'; import { Box } from '@mui/material'; -import { useTheme, Theme } from '@mui/material/styles'; import { SimpleModal } from './Modals/SimpleModal'; import { ModalFee } from './Modals/ModalFee'; import { ModalDivider } from './Modals/ModalDivider'; -import { backDropStyles, modalStyles } from '../../.storybook/storiesStyles'; - -const storybookStyles = (theme: Theme, isStorybook?: boolean, backdropProps?: object) => - isStorybook - ? { - backdropProps: { ...backDropStyles(theme), ...backdropProps }, - sx: modalStyles(theme), - } - : {}; export const ConfirmTx: FCWithChildren<{ open: boolean; @@ -23,26 +13,21 @@ export const ConfirmTx: FCWithChildren<{ onConfirm: () => Promise; onClose?: () => void; onPrev: () => void; - isStorybook?: boolean; children?: React.ReactNode; -}> = ({ open, fee, onConfirm, onClose, header, subheader, onPrev, children, isStorybook }) => { - const theme = useTheme(); - return ( - - - {children} - - - - - ); -}; +}> = ({ open, fee, onConfirm, onClose, header, subheader, onPrev, children }) => ( + + + {children} + + + + +); diff --git a/nym-wallet/src/components/CurrencyFormFieldWithPaste.tsx b/nym-wallet/src/components/CurrencyFormFieldWithPaste.tsx index d21dfc27b9..addade1079 100644 --- a/nym-wallet/src/components/CurrencyFormFieldWithPaste.tsx +++ b/nym-wallet/src/components/CurrencyFormFieldWithPaste.tsx @@ -95,6 +95,7 @@ export const CurrencyFormFieldWithPaste = ({ useEffect(() => { const handleKeyDown = async (e: KeyboardEvent) => { + if (e.defaultPrevented) return; if (inputRef.current && document.activeElement === inputRef.current) { if ((e.metaKey || e.ctrlKey) && e.key === 'v') { e.preventDefault(); @@ -120,7 +121,7 @@ export const CurrencyFormFieldWithPaste = ({ }, [denom, onChanged]); return ( - + ; - -export const Default = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - const theme = useTheme(); - return ( - <> - -

Lorem ipsum

- - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis - sunt velit elit do minim mollit non duis reprehenderit. Eiusmod dolore adipisicing ex nostrud consectetur - culpa exercitation do. Ad elit esse ipsum aliqua labore irure laborum qui culpa. - - - setOpen(false)} - backdropProps={backDropStyles(theme)} - sx={modalStyles(theme)} - /> - - ); -}; diff --git a/nym-wallet/src/components/Delegation/DelegateModal.tsx b/nym-wallet/src/components/Delegation/DelegateModal.tsx index b923c20136..5599b452de 100644 --- a/nym-wallet/src/components/Delegation/DelegateModal.tsx +++ b/nym-wallet/src/components/Delegation/DelegateModal.tsx @@ -219,7 +219,7 @@ export const DelegateModal: FCWithChildren<{ )} - + ); @@ -249,10 +249,12 @@ export const DelegateModal: FCWithChildren<{ header={header || 'Delegate'} okLabel={buttonText || 'Delegate stake'} okDisabled={!isValidated} + dense + accent="primary" sx={sx} backdropProps={backdropProps} > - + - + {hasVestingContract && setTokenPool(pool)} />} - + @@ -299,7 +301,11 @@ export const DelegateModal: FCWithChildren<{ hidden divider /> - + ); }; diff --git a/nym-wallet/src/components/Delegation/DelegationActions.stories.tsx b/nym-wallet/src/components/Delegation/DelegationActions.stories.tsx deleted file mode 100644 index 29d29fe4ac..0000000000 --- a/nym-wallet/src/components/Delegation/DelegationActions.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { ComponentMeta } from '@storybook/react'; - -import { DelegationActions } from './DelegationActions'; - -export default { - title: 'Delegation/Components/Delegation List Item Actions', - component: DelegationActions, -} as ComponentMeta; - -export const Default = () => ; - -export const RedeemingDisabled = () => ; - -export const PendingDelegation = () => ; - -export const PendingUndelegation = () => ( - -); diff --git a/nym-wallet/src/components/Delegation/DelegationItem.tsx b/nym-wallet/src/components/Delegation/DelegationItem.tsx deleted file mode 100644 index 43b118eb61..0000000000 --- a/nym-wallet/src/components/Delegation/DelegationItem.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React from 'react'; -import { Box, Chip, IconButton, TableCell, TableRow, Tooltip, Typography } from '@mui/material'; -import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; -import { decimalToPercentage, DelegationWithEverything } from '@nymproject/types'; -import { LockOutlined, WarningAmberOutlined } from '@mui/icons-material'; -import { isDelegation, useDelegationContext } from 'src/context/delegations'; -import { toPercentIntegerString } from 'src/utils'; -import { format } from 'date-fns'; -import { Undelegate } from 'src/svg-icons'; -import { DelegationListItemActions, DelegationsActionsMenu } from './DelegationActions'; - -const getStakeSaturation = (item: DelegationWithEverything) => - !item.stake_saturation ? '-' : `${decimalToPercentage(item.stake_saturation)}%`; - -const getRewardValue = (item: DelegationWithEverything) => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { unclaimed_rewards } = item; - return !unclaimed_rewards ? '-' : `${unclaimed_rewards.amount} ${unclaimed_rewards.denom}`; -}; - -export const DelegationItem = ({ - item, - explorerUrl, - nodeIsUnbonded, - onItemActionClick, -}: { - item: DelegationWithEverything; - explorerUrl: string; - nodeIsUnbonded: boolean; - onItemActionClick?: (item: DelegationWithEverything, action: DelegationListItemActions) => void; -}) => { - const { setDelegationItemErrors } = useDelegationContext(); - - const operatingCost = isDelegation(item) && item.cost_params?.interval_operating_cost; - - const tooltipText = () => { - if (nodeIsUnbonded) { - return 'This node has unbonded and it does not exist anymore. You need to undelegate from it to get your stake and outstanding rewards (if any) back.'; - } - return ''; - }; - - return ( - - - - {nodeIsUnbonded ? ( - '-' - ) : ( - - {item.errors && ( - - setDelegationItemErrors({ nodeId: item.node_identity, errors: item.errors! })} - > - - - - )} - - - )} - - - {isDelegation(item) && (!item.avg_uptime_percent ? '-' : `${item.avg_uptime_percent}%`)} - - - {isDelegation(item) && - (!item.cost_params?.profit_margin_percent - ? '-' - : `${toPercentIntegerString(item.cost_params.profit_margin_percent)}%`)} - - - - {operatingCost ? `${operatingCost.amount} ${operatingCost.denom}` : '-'} - - - {getStakeSaturation(item)} - - {item.delegated_on_iso_datetime && format(new Date(item.delegated_on_iso_datetime), 'dd/MM/yyyy')} - - - - {isDelegation(item) ? `${item.amount.amount} ${item.amount.denom}` : '-'} - - - {getRewardValue(item)} - - {item.uses_vesting_contract_tokens && ( - - - - )} - - - {!item.pending_events.length && !nodeIsUnbonded && ( - (onItemActionClick ? onItemActionClick(item, action) : undefined)} - disableRedeemingRewards={!item.unclaimed_rewards || item.unclaimed_rewards.amount === '0'} - disableDelegateMore={item.mixnode_is_unbonding} - /> - )} - {!item.pending_events.length && nodeIsUnbonded && ( - t.palette.nym.nymWallet.text.main }}> - (onItemActionClick ? onItemActionClick(item, 'undelegate') : undefined)} /> - - )} - {item.pending_events.length > 0 && ( - - - - )} - - - - ); -}; diff --git a/nym-wallet/src/components/Delegation/DelegationList.stories.tsx b/nym-wallet/src/components/Delegation/DelegationList.stories.tsx deleted file mode 100644 index 4b24a3dcfe..0000000000 --- a/nym-wallet/src/components/Delegation/DelegationList.stories.tsx +++ /dev/null @@ -1,311 +0,0 @@ -import React from 'react'; -import { ComponentMeta } from '@storybook/react'; - -import { DelegationWithEverything } from '@nymproject/types'; -import { DelegationList } from './DelegationList'; - -export default { - title: 'Delegation/Components/Delegation List', - component: DelegationList, -} as ComponentMeta; - -const explorerUrl = 'https://sandbox-explorer.nymtech.net/network-components/mixnodes'; - -export const items: DelegationWithEverything[] = [ - { - mix_id: 1, - node_identity: 'FiojKW7oY9WQmLCiYAsCA21tpowZHS6zcUoyYm319p6Z', - delegated_on_iso_datetime: new Date(2021, 1, 1).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - amount: { amount: '10', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(100), - stake_saturation: '0.25', - avg_uptime_percent: 0.5, - uses_vesting_contract_tokens: false, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 2, - node_identity: 'DT8S942S8AQs2zKHS9SVo1GyHmuca3pfL2uLhLksJ3D8', - amount: { amount: '1010', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '200', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.43', - avg_uptime_percent: 0.22, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 3, - node_identity: '', - amount: { amount: '300', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '50', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '300', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.5', - avg_uptime_percent: 0.1, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 4, - node_identity: 'DT8S942S8AQs2zKHS9SVo1GyHmuca3pfL2uLhLksJ3D8', - amount: { amount: '201', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '60', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '202', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.5', - avg_uptime_percent: 0.1, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 5, - node_identity: 'DT8S942S8AQs2zKHS9SVo1GyHmuca3pfL2uLhLksJ3D8', - amount: { amount: '100', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '80', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.5', - avg_uptime_percent: 0.1, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 6, - node_identity: '', - amount: { amount: '202', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.8', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.5', - avg_uptime_percent: 0.1, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 7, - node_identity: 'FiojKW7oY9WQmLCiYAsCA21tpowZHS6zcUoyYm319p6Z', - delegated_on_iso_datetime: new Date(2021, 1, 1).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - amount: { amount: '202', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.59', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(100), - stake_saturation: '0.5', - avg_uptime_percent: 0.5, - uses_vesting_contract_tokens: false, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 8, - node_identity: 'DT8S942S8AQs2zKHS9SVo1GyHmuca3pfL2uLhLksJ3D8', - amount: { amount: '100', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.9', - avg_uptime_percent: 0.1, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 9, - node_identity: '', - amount: { amount: '1000', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.4', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.9', - avg_uptime_percent: 0.1, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 10, - node_identity: 'DT8S942S8AQs2zKHS9SVo1GyHmuca3pfL2uLhLksJ3D8', - amount: { amount: '100', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.5', - avg_uptime_percent: 0.1, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 11, - node_identity: 'DT8S942S8AQs2zKHS9SVo1GyHmuca3pfL2uLhLksJ3D8', - amount: { amount: '100', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.56', - avg_uptime_percent: 0.9, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, - { - mix_id: 12, - node_identity: '', - amount: { amount: '100', denom: 'nym' }, - delegated_on_iso_datetime: new Date(2021, 1, 2).toDateString(), - unclaimed_rewards: { amount: '0.05', denom: 'nym' }, - cost_params: { - profit_margin_percent: '0.1122323949234', - interval_operating_cost: { - amount: '40', - denom: 'nym', - }, - }, - accumulated_by_delegates: { amount: '50', denom: 'nym' }, - accumulated_by_operator: { amount: '100', denom: 'nym' }, - owner: '', - block_height: BigInt(4000), - stake_saturation: '0.5', - avg_uptime_percent: 0.1, - uses_vesting_contract_tokens: true, - pending_events: [], - mixnode_is_unbonding: true, - errors: null, - }, -]; - -export const WithData = () => ; - -export const Empty = () => ; - -export const OneItem = () => ; - -export const Loading = () => ; diff --git a/nym-wallet/src/components/Delegation/DelegationList.tsx b/nym-wallet/src/components/Delegation/DelegationList.tsx index 3e84331f19..51ad848a49 100644 --- a/nym-wallet/src/components/Delegation/DelegationList.tsx +++ b/nym-wallet/src/components/Delegation/DelegationList.tsx @@ -4,24 +4,36 @@ import { AlertTitle, Box, Button, + Collapse, + FormControl, + IconButton, + InputLabel, + MenuItem, + Select, + Skeleton, + Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, - TableSortLabel, + TextField, + Tooltip, Typography, } from '@mui/material'; -import { visuallyHidden } from '@mui/utils'; -import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; -import { DelegationWithEverything } from '@nymproject/types'; +import { alpha } from '@mui/material/styles'; +import { KeyboardArrowDown, KeyboardArrowUp, LockOutlined, WarningAmberOutlined } from '@mui/icons-material'; +import { decimalToFloatApproximation, decimalToPercentage, DelegationWithEverything } from '@nymproject/types'; import { useSortDelegations } from 'src/hooks/useSortDelegations'; import { useNavigate } from 'react-router-dom'; -import { DelegationListItemActions } from './DelegationActions'; -import { DelegationItem } from './DelegationItem'; -import { PendingDelegationItem } from './PendingDelegationItem'; -import { LoadingModal } from '../Modals/LoadingModal'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; +import { format } from 'date-fns'; +import { Undelegate } from 'src/svg-icons'; +import { toPercentIntegerString } from 'src/utils'; +import { InfoTooltip } from '../InfoToolTip'; +import { DelegationListItemActions, DelegationsActionsMenu } from './DelegationActions'; +import { PendingDelegationCard } from './PendingDelegationCard'; import { isDelegation, isPendingDelegation, TDelegations, useDelegationContext } from '../../context/delegations'; import { ErrorModal } from '../Modals/ErrorModal'; @@ -29,126 +41,37 @@ export type Order = 'asc' | 'desc'; type AdditionalTypes = { profit_margin_percent: number; operating_cost: number }; export type SortingKeys = keyof AdditionalTypes | keyof DelegationWithEverything; -// Helper function to check if a delegation item should be filtered const shouldBeFiltered = (item: any): boolean => { - // For regular delegations, filter out placeholders if (isDelegation(item)) { - // Check if node_identity is empty or just placeholders if (!item.node_identity || item.node_identity === '-' || item.node_identity === '...') { return true; } - - // Check if uptime is a placeholder dash if (typeof item.avg_uptime_percent === 'string' && item.avg_uptime_percent === '-') { return true; } } - // For pending delegations, keep "Delegate" events but filter out "Undelegate" events with empty node_identity if (isPendingDelegation(item)) { - // If it's an undelegate event with empty node_identity, filter it out if ((!item.node_identity || item.node_identity === '') && item.event && item.event.kind === 'Undelegate') { return true; } - - // Keep all other pending events (including new delegation events) return false; } return false; }; -interface EnhancedTableProps { - onRequestSort: (event: React.MouseEvent, property: string) => void; - order: Order; - orderBy: string; -} - -interface HeadCell { - id: string; - label: string | React.ReactNode; - sortable: boolean; - disablePadding?: boolean; - align: 'left' | 'center' | 'right'; - width?: string; -} - -const headCells: HeadCell[] = [ - { id: 'node_identity', label: 'Node ID', sortable: true, align: 'left', width: '15%' }, - { id: 'avg_uptime_percent', label: 'Routing score', sortable: true, align: 'left', width: '10%' }, - { id: 'profit_margin_percent', label: 'Profit margin', sortable: true, align: 'left', width: '10%' }, - { id: 'operating_cost', label: 'Operating Cost', sortable: true, align: 'left', width: '12%' }, - { id: 'stake_saturation', label: 'Stake saturation', sortable: true, align: 'left', width: '10%' }, - { - id: 'delegated_on_iso_datetime', - label: 'Delegated on', - sortable: true, - align: 'left', - width: '10%', - }, - { id: 'amount', label: 'Delegation', sortable: true, align: 'left', width: '12%' }, - { id: 'unclaimed_rewards', label: 'Reward', sortable: true, align: 'left', width: '10%' }, - { id: 'uses_locked_tokens', label: '', sortable: false, align: 'left', width: '8%' }, +const SORT_FIELD_OPTIONS: { id: SortingKeys; label: string }[] = [ + { id: 'delegated_on_iso_datetime', label: 'Delegated on' }, + { id: 'node_identity', label: 'Node ID' }, + { id: 'avg_uptime_percent', label: 'Routing score' }, + { id: 'profit_margin_percent', label: 'Profit margin' }, + { id: 'operating_cost', label: 'Operating cost' }, + { id: 'stake_saturation', label: 'Stake saturation' }, + { id: 'amount', label: 'Delegation' }, + { id: 'unclaimed_rewards', label: 'Reward' }, ]; -const EnhancedTableHead: FCWithChildren = ({ order, orderBy, onRequestSort }) => { - const createSortHandler = (property: string) => (event: React.MouseEvent) => { - onRequestSort(event, property); - }; - - return ( - - - {headCells.map((headCell) => ( - - - {headCell.label} - {orderBy === headCell.id ? ( - - {order === 'desc' ? 'sorted descending' : 'sorted ascending'} - - ) : null} - - - ))} - - - Actions - - - - - ); -}; - const hasPruningError = (item: any): boolean => { if (!isDelegation(item) || !item.errors) return false; @@ -158,43 +81,69 @@ const hasPruningError = (item: any): boolean => { ); }; +const getStakeSaturation = (item: DelegationWithEverything) => + !item.stake_saturation ? '-' : `${decimalToPercentage(item.stake_saturation)}%`; + +const getRewardValue = (item: DelegationWithEverything) => { + const { unclaimed_rewards } = item; + return !unclaimed_rewards ? '-' : `${unclaimed_rewards.amount} ${unclaimed_rewards.denom}`; +}; + +const saturationNumeric = (item: DelegationWithEverything): number | undefined => { + if (!item.stake_saturation) return undefined; + return decimalToFloatApproximation(item.stake_saturation); +}; + export const DelegationList: FCWithChildren<{ - isLoading?: boolean; items: TDelegations; onItemActionClick?: (item: DelegationWithEverything, action: DelegationListItemActions) => void; explorerUrl: string; - // eslint-disable-next-line @typescript-eslint/no-unused-vars -}> = ({ isLoading, items, onItemActionClick, explorerUrl }) => { + nextEpoch?: string | Error; +}> = ({ items, onItemActionClick, explorerUrl, nextEpoch }) => { const [order, setOrder] = React.useState('asc'); const [orderBy, setOrderBy] = React.useState('delegated_on_iso_datetime'); + const [identityFilter, setIdentityFilter] = React.useState(''); + const [expandedKey, setExpandedKey] = React.useState(null); const navigate = useNavigate(); - const { delegationItemErrors, setDelegationItemErrors } = useDelegationContext(); + const { + delegationItemErrors, + setDelegationItemErrors, + totalDelegations, + totalRewards, + totalDelegationsAndRewards, + isLoading: delegationsSummaryLoading, + } = useDelegationContext(); - const handleRequestSort = (_: React.MouseEvent, property: any) => { - const isAsc = orderBy === property && order === 'asc'; - setOrder(isAsc ? 'desc' : 'asc'); - setOrderBy(property); - }; - - // Get sorted items const sorted = useSortDelegations(items, order, orderBy); - // Filter out empty placeholder rows const filteredItems = React.useMemo(() => { if (!sorted) return []; return sorted.filter((item) => !shouldBeFiltered(item)); }, [sorted]); - // Check if any delegations have pruning errors + const activeDelegations = React.useMemo( + () => filteredItems.filter((item): item is DelegationWithEverything => isDelegation(item)), + [filteredItems], + ); + + const pendingItems = React.useMemo(() => filteredItems.filter((item) => isPendingDelegation(item)), [filteredItems]); + + const searchNeedle = identityFilter.trim().toLowerCase(); + + const displayedDelegations = React.useMemo(() => { + if (!searchNeedle) return activeDelegations; + return activeDelegations.filter((d) => d.node_identity.toLowerCase().includes(searchNeedle)); + }, [activeDelegations, searchNeedle]); + + const activeCount = activeDelegations.length; + const hasPruningErrors = React.useMemo(() => filteredItems?.some((item) => hasPruningError(item)), [filteredItems]); - // Navigate to settings page const navigateToSettings = () => { navigate('/settings'); }; - // Format error message for display const formatErrorMessage = (message: string) => { if (message.includes('height') && message.includes('not available')) { return 'Due to pruning strategies from validators, please navigate to the Settings tab and change your RPC node for your validator to retrieve your delegations.'; @@ -202,9 +151,22 @@ export const DelegationList: FCWithChildren<{ return message; }; + const pendingKey = (item: any, suffix: string) => + `pending-${item.event?.mix_id}-${item.event?.address ?? ''}-${item.event?.kind ?? ''}-${ + item.node_identity ?? '' + }-${suffix}`; + + const nextEpochLine = + nextEpoch instanceof Error || !nextEpoch ? null : ( + + Next epoch starts at {nextEpoch} + + ); + + const emptyTableMessage = searchNeedle ? 'No delegations match your search.' : 'No delegations to show.'; + return ( <> - {/* Display pruning error alert at the top if needed */} {hasPruningErrors && ( )} - {/* Add horizontal scrolling to the table container */} - - {isLoading && } - setDelegationItemErrors(undefined)} - /> - - - - {filteredItems?.length - ? filteredItems.map((item: any, _index: number) => { - if (isPendingDelegation(item)) { - const pendingKey = `pending-${item.event.mix_id}-${ - item.event.address - }-${Date.now()}-${Math.random()}`; + + + + + Delegations + + + {activeCount} active + + - if ( - item.event && - item.event.kind === 'Delegate' && - (!item.node_identity || item.node_identity === '') - ) { - return ( - - ); - } + + + setIdentityFilter(e.target.value)} + sx={{ + minWidth: { xs: '100%', md: 220 }, + flex: { md: '1 1 200px' }, + '& .MuiOutlinedInput-root': { borderRadius: 2 }, + }} + /> + + Sort by + + + + Order + + + + {nextEpochLine ? {nextEpochLine} : null} + + - return ; + + `1px solid ${t.palette.divider}`, + bgcolor: (t) => + t.palette.mode === 'dark' ? 'nym.nymWallet.nav.background' : 'nym.nymWallet.background.subtle', + p: 2.5, + }} + > + + + + Total delegations + + + + {delegationsSummaryLoading ? : totalDelegationsAndRewards ?? '-'} + + + `1px solid ${t.palette.divider}`, + bgcolor: (t) => + t.palette.mode === 'dark' ? 'nym.nymWallet.nav.background' : 'nym.nymWallet.background.subtle', + p: 2.5, + }} + > + + + + Original delegations + + + + {delegationsSummaryLoading ? : totalDelegations ?? '-'} + + + `1px solid ${t.palette.divider}`, + bgcolor: (t) => + t.palette.mode === 'dark' ? 'nym.nymWallet.nav.background' : 'nym.nymWallet.background.subtle', + p: 2.5, + }} + > + + + + Total rewards + + + + {delegationsSummaryLoading ? : totalRewards ?? '-'} + + + + + {pendingItems.length > 0 && ( + + + Pending + + + {pendingItems.map((item: any, index: number) => { + if ( + item.event && + item.event.kind === 'Delegate' && + (!item.node_identity || item.node_identity === '') + ) { + return ( + + ); + } + + return ( + + ); + })} + + + )} + + `1px solid ${t.palette.divider}`, + bgcolor: (t) => + t.palette.mode === 'dark' ? 'nym.nymWallet.nav.background' : 'nym.nymWallet.background.subtle', + }} + > + setDelegationItemErrors(undefined)} + /> +
+ + + Node + + Amount + + + Saturation + + + Reward + + + Actions + + + + + {displayedDelegations.length === 0 ? ( + + + + {emptyTableMessage} + + + + ) : ( + displayedDelegations.map((item) => { + const rowKey = `${item.mix_id}-${item.node_identity}`; + const isOpen = expandedKey === rowKey; + const nodeIsUnbonded = Boolean(!item.node_identity); + const satNum = saturationNumeric(item); + let satColor: 'text.secondary' | 'error.main' | 'success.main' = 'text.secondary'; + if (satNum !== undefined) { + satColor = satNum > 1 ? 'error.main' : 'success.main'; } - if (isDelegation(item)) { - if (!item.node_identity || item.node_identity === '-' || item.node_identity === '...') { - return null; - } + const operatingCost = item.cost_params?.interval_operating_cost; + const uptime = item.avg_uptime_percent; + const routingDisplay = uptime != null && String(uptime) !== '-' ? `${uptime}%` : '-'; + const marginDisplay = item.cost_params?.profit_margin_percent + ? `${toPercentIntegerString(item.cost_params.profit_margin_percent)}%` + : '-'; + const costDisplay = operatingCost ? `${operatingCost.amount} ${operatingCost.denom}` : '-'; + const delegatedDisplay = item.delegated_on_iso_datetime + ? format(new Date(item.delegated_on_iso_datetime), 'dd/MM/yyyy') + : '-'; - return ( - - ); - } + const unbondedTooltip = + 'This node has unbonded and it does not exist anymore. You need to undelegate from it to get your stake and outstanding rewards (if any) back.'; - return null; + return ( + + *': { borderBottom: 'unset' } }}> + + + setExpandedKey(isOpen ? null : rowKey)} + > + {isOpen ? : } + + + {item.errors && ( + + + setDelegationItemErrors({ nodeId: item.node_identity, errors: item.errors! }) + } + > + + + + )} + {item.uses_vesting_contract_tokens && ( + + + + )} + {nodeIsUnbonded ? ( + + - + + ) : ( + + )} + + + + + {item.amount.amount} {item.amount.denom} + + + {getStakeSaturation(item)} + + + {getRewardValue(item)} + + + {!item.pending_events.length && !nodeIsUnbonded && ( + + onItemActionClick ? onItemActionClick(item, action) : undefined + } + disableRedeemingRewards={!item.unclaimed_rewards || item.unclaimed_rewards.amount === '0'} + disableDelegateMore={item.mixnode_is_unbonding} + /> + )} + {!item.pending_events.length && nodeIsUnbonded && ( + t.palette.nym.nymWallet.text.main }} size="small"> + (onItemActionClick ? onItemActionClick(item, 'undelegate') : undefined)} + /> + + )} + {item.pending_events.length > 0 && ( + + + Pending events + + + )} + + + + + + + `1px solid ${alpha(t.palette.divider, t.palette.mode === 'dark' ? 0.35 : 0.5)}`, + bgcolor: (t) => + t.palette.mode === 'dark' + ? alpha(t.palette.common.white, 0.04) + : alpha(t.palette.common.black, 0.03), + }} + > +
+ + + + Routing + + + Margin + + + NYM cost + + + Delegated on + + + + + + + + {routingDisplay} + + + + + {marginDisplay} + + + + + {costDisplay} + + + + + {delegatedDisplay} + + + + +
+
+ + + + + ); }) - : null} - - - + )} + + + + ); }; diff --git a/nym-wallet/src/components/Delegation/DelegationModal.stories.tsx b/nym-wallet/src/components/Delegation/DelegationModal.stories.tsx deleted file mode 100644 index bba93fca5a..0000000000 --- a/nym-wallet/src/components/Delegation/DelegationModal.stories.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import React from 'react'; -import { ComponentMeta } from '@storybook/react'; - -import { Paper, Button } from '@mui/material'; -import { useTheme, Theme } from '@mui/material/styles'; -import { Delegations } from './Delegations'; -import { items } from './DelegationList.stories'; -import { DelegationModal } from './DelegationModal'; -import { backDropStyles, modalStyles } from '../../../.storybook/storiesStyles'; - -const explorerUrl = 'https://sandbox-explorer.nymtech.net'; - -const storybookStyles = (theme: Theme) => ({ - backdropProps: backDropStyles(theme), - sx: modalStyles(theme), -}); - -export default { - title: 'Delegation/Components/Delegation Modals', - component: Delegations, -} as ComponentMeta; - -const transaction = { - url: 'https://sandbox-blocks.nymtech.net/transactions/11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CFB', - hash: '11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CFB', -}; -// Another transaction for Dark Theme to avoid duplicate key errors in rendering -const transactionForDarkTheme = { - url: 'https://sandbox-blocks.nymtech.net/transactions/11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CFO', - hash: '11ED7B9E21534A9421834F52FED5103DC6E982949C06335F5E12EFC71DAF0CF0', -}; - -const Content: FCWithChildren<{ children: React.ReactElement; handleClick: () => void }> = ({ - children, - handleClick, -}) => ( - <> - -

Your Delegations

- - -
- {children} - -); - -export const Loading = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - const theme = useTheme(); - return ( - - setOpen(false)} - status="loading" - action="delegate" - {...storybookStyles(theme)} - /> - - ); -}; - -export const DelegateSuccess = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - const theme = useTheme(); - return ( - - setOpen(false)} - status="success" - action="delegate" - message="You delegated 5 NYM" - transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]} - {...storybookStyles(theme)} - /> - - ); -}; - -export const UndelegateSuccess = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - const theme = useTheme(); - return ( - - setOpen(false)} - status="success" - action="undelegate" - message="You undelegated 5 NYM" - transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]} - {...storybookStyles(theme)} - /> - - ); -}; - -export const RedeemSuccess = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - const theme = useTheme(); - return ( - - setOpen(false)} - status="success" - action="redeem" - message="42 NYM" - transactions={ - theme.palette.mode === 'light' - ? [transaction, transaction] - : [transactionForDarkTheme, transactionForDarkTheme] - } - {...storybookStyles(theme)} - /> - - ); -}; - -export const RedeemWithVestedSuccess = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - const theme = useTheme(); - return ( - - setOpen(false)} - status="success" - action="redeem" - message="42 NYM" - transactions={ - theme.palette.mode === 'light' - ? [transaction, transaction] - : [transactionForDarkTheme, transactionForDarkTheme] - } - {...storybookStyles(theme)} - /> - - ); -}; - -export const RedeemAllSuccess = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - const theme = useTheme(); - return ( - - setOpen(false)} - status="success" - action="redeem-all" - message="42 NYM" - transactions={ - theme.palette.mode === 'light' - ? [transaction, transaction] - : [transactionForDarkTheme, transactionForDarkTheme] - } - {...storybookStyles(theme)} - /> - - ); -}; - -export const Error = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - const theme = useTheme(); - return ( - - setOpen(false)} - status="error" - action="redeem-all" - message="Minim esse veniam Lorem id velit Lorem eu eu est. Excepteur labore sunt do proident proident sint aliquip consequat Lorem sint non nulla ad excepteur." - transactions={theme.palette.mode === 'light' ? [transaction] : [transactionForDarkTheme]} - {...storybookStyles(theme)} - /> - - ); -}; diff --git a/nym-wallet/src/components/Delegation/Delegations.stories.tsx b/nym-wallet/src/components/Delegation/Delegations.stories.tsx deleted file mode 100644 index 99a37271bb..0000000000 --- a/nym-wallet/src/components/Delegation/Delegations.stories.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { ComponentMeta } from '@storybook/react'; - -import { Paper } from '@mui/material'; -import { Delegations } from './Delegations'; -import { items } from './DelegationList.stories'; - -const explorerUrl = 'https://sandbox-explorer.nymtech.net'; - -export default { - title: 'Delegation/Components/Delegations', - component: Delegations, -} as ComponentMeta; - -export const Default = () => ( - -

Your Delegations

- -
-); - -export const Empty = () => ( - -

Your Delegations

- -
-); diff --git a/nym-wallet/src/components/Delegation/Delegations.tsx b/nym-wallet/src/components/Delegation/Delegations.tsx index 2234dcbc6a..88916dcdfa 100644 --- a/nym-wallet/src/components/Delegation/Delegations.tsx +++ b/nym-wallet/src/components/Delegation/Delegations.tsx @@ -6,18 +6,12 @@ import { DelegationList } from './DelegationList'; import { DelegationListItemActions } from './DelegationActions'; export const Delegations: FCWithChildren<{ - isLoading?: boolean; items: DelegationWithEverything[]; explorerUrl: string; onDelegationItemActionClick?: (item: DelegationWithEverything, action: DelegationListItemActions) => void; -}> = ({ isLoading, items, explorerUrl, onDelegationItemActionClick }) => ( +}> = ({ items, explorerUrl, onDelegationItemActionClick }) => ( <> - + Check the{' '} diff --git a/nym-wallet/src/components/Delegation/Modals.stories.tsx b/nym-wallet/src/components/Delegation/Modals.stories.tsx deleted file mode 100644 index 630d023217..0000000000 --- a/nym-wallet/src/components/Delegation/Modals.stories.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import React from 'react'; - -import { Button, Paper, Typography } from '@mui/material'; -import { useTheme, Theme } from '@mui/material/styles'; -import { DelegateModal } from './DelegateModal'; -import { UndelegateModal } from './UndelegateModal'; -import { backDropStyles, modalStyles } from '../../../.storybook/storiesStyles'; - -const storybookStyles = (theme: Theme) => ({ - backdropProps: backDropStyles(theme), - sx: modalStyles(theme), -}); - -export default { - title: 'Delegation/Components/Action Modals', -}; - -const Background: FCWithChildren<{ onOpen: () => void }> = ({ onOpen }) => { - const theme = useTheme(); - return ( - -

Lorem ipsum

- - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis sunt - velit elit do minim mollit non duis reprehenderit. Eiusmod dolore adipisicing ex nostrud consectetur culpa - exercitation do. Ad elit esse ipsum aliqua labore irure laborum qui culpa. - - - Occaecat commodo excepteur anim ut officia dolor laboris dolore id occaecat enim qui eiusmod occaecat aliquip ad - tempor. Labore amet laborum magna amet consequat dolor cupidatat in consequat sunt aliquip magna laboris tempor - culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna elit ut - mollit. - - - Labore voluptate elit amet ipsum qui officia duis in et occaecat culpa ex do non labore mollit. Cillum cupidatat - duis ea dolore laboris laboris sunt duis anim consectetur cupidatat nulla ad minim sunt ea. Aliqua amet commodo - est irure sint magna sunt. Pariatur dolore commodo labore quis incididunt proident duis voluptate exercitation - in duis. Occaecat aliqua laboris reprehenderit nostrud est aute pariatur fugiat anim. Dolore sunt cillum ea - aliquip consectetur laborum ipsum qui veniam Lorem consectetur adipisicing velit magna aute. Amet tempor quis - excepteur minim culpa velit Lorem enim ad. - - - Mollit laborum exercitation excepteur laboris adipisicing ipsum veniam cillum mollit voluptate do. Amet et anim - Lorem mollit minim duis cupidatat non. Consectetur sit deserunt nisi nisi non excepteur dolor eiusmod aute aute - irure anim dolore ipsum et veniam. - -
- ); -}; - -export const Delegate = () => { - const [open, setOpen] = React.useState(false); - const theme = useTheme(); - return ( - <> - setOpen(true)} /> - setOpen(false)} - onOk={async () => setOpen(false)} - denom="nym" - estimatedReward={50.423} - accountBalance="425.2345053" - nodeUptimePercentage={99.28394} - profitMarginPercentage="11.12334234" - rewardInterval="monthlyish" - hasVestingContract={false} - {...storybookStyles(theme)} - /> - - ); -}; - -export const DelegateBelowMinimum = () => { - const [open, setOpen] = React.useState(false); - const theme = useTheme(); - return ( - <> - setOpen(true)} /> - setOpen(false)} - onOk={async () => setOpen(false)} - denom="nym" - estimatedReward={425.2345053} - nodeUptimePercentage={99.28394} - profitMarginPercentage="11.12334234" - rewardInterval="monthlyish" - initialAmount="0.1" - hasVestingContract={false} - {...storybookStyles(theme)} - /> - - ); -}; - -export const DelegateMore = () => { - const [open, setOpen] = React.useState(false); - const theme = useTheme(); - return ( - <> - setOpen(true)} /> - setOpen(false)} - onOk={async () => setOpen(false)} - header="Delegate more" - buttonText="Delegate more" - denom="nym" - estimatedReward={50.423} - accountBalance="425.2345053" - nodeUptimePercentage={99.28394} - profitMarginPercentage="11.12334234" - rewardInterval="monthlyish" - hasVestingContract={false} - {...storybookStyles(theme)} - /> - - ); -}; - -export const Undelegate = () => { - const [open, setOpen] = React.useState(false); - const theme = useTheme(); - return ( - <> - setOpen(true)} /> - setOpen(false)} - onOk={async () => setOpen(false)} - currency="nym" - amount={150} - mixId={1234} - identityKey="AA6RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujyxx" - usesVestingContractTokens={false} - {...storybookStyles(theme)} - /> - - ); -}; diff --git a/nym-wallet/src/components/Delegation/PendingDelegationCard.tsx b/nym-wallet/src/components/Delegation/PendingDelegationCard.tsx new file mode 100644 index 0000000000..6af5f797ec --- /dev/null +++ b/nym-wallet/src/components/Delegation/PendingDelegationCard.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { Box, Chip, Paper, Stack, Tooltip, Typography } from '@mui/material'; +import { WrappedDelegationEvent } from '@nymproject/types'; +import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; + +export const PendingDelegationCard = ({ item, explorerUrl }: { item: WrappedDelegationEvent; explorerUrl: string }) => ( + (t.palette.mode === 'dark' ? 'nym.nymWallet.nav.background' : 'nym.nymWallet.background.subtle'), + borderColor: 'divider', + }} + > + + + + {item.event.amount?.amount} {item.event.amount?.denom?.toUpperCase() ?? 'NYM'} + + + + Your delegation of {item.event.amount?.amount} {item.event.amount?.denom} will take effect when the new + epoch starts. There is a new epoch every hour. + + } + arrow + PopperProps={{ + sx: { + '& .MuiTooltip-tooltip': { textAlign: 'left' }, + }, + }} + > + + + + +); diff --git a/nym-wallet/src/components/Delegation/PendingDelegationItem.tsx b/nym-wallet/src/components/Delegation/PendingDelegationItem.tsx deleted file mode 100644 index fc3c070625..0000000000 --- a/nym-wallet/src/components/Delegation/PendingDelegationItem.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import { Box, Chip, TableCell, TableRow, Tooltip } from '@mui/material'; -import { WrappedDelegationEvent } from '@nymproject/types'; -import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; - -export const PendingDelegationItem = ({ item, explorerUrl }: { item: WrappedDelegationEvent; explorerUrl: string }) => ( - - - - - - - - - - - - - - - - {item.event.amount?.amount} NYM - - - - - - - Your delegation of {item.event.amount?.amount} {item.event.amount?.denom} will take effect when the new - epoch starts. There is a new epoch every hour. - - } - arrow - PopperProps={{ - sx: { - '& .MuiTooltip-tooltip': { - textAlign: 'center', - }, - }, - }} - > - - - - - -); diff --git a/nym-wallet/src/components/FeeWarning.stories.tsx b/nym-wallet/src/components/FeeWarning.stories.tsx deleted file mode 100644 index 7a553aae79..0000000000 --- a/nym-wallet/src/components/FeeWarning.stories.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import * as React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Box } from '@mui/material'; -import { BalanceWarning } from './FeeWarning'; - -export default { - title: 'Wallet / Balance warning', - component: BalanceWarning, -} as ComponentMeta; - -const Template: ComponentStory = (args) => ( - - - -); - -export const WithWarning = Template.bind({}); -WithWarning.args = { - fee: '200', -}; diff --git a/nym-wallet/src/components/LogViewer/index.tsx b/nym-wallet/src/components/LogViewer/index.tsx index b796715324..424280f15d 100644 --- a/nym-wallet/src/components/LogViewer/index.tsx +++ b/nym-wallet/src/components/LogViewer/index.tsx @@ -1,6 +1,6 @@ import React, { FC, useEffect, useRef, useState } from 'react'; import type { UnlistenFn } from '@tauri-apps/api/event'; -import { listen } from '@tauri-apps/api/event'; +import { getCurrentWebview } from '@tauri-apps/api/webview'; import { Box, Paper, @@ -90,32 +90,43 @@ interface RecordPayload { export const LogViewer: FC = () => { const theme = useTheme(); - const unlisten = useRef(); + const unlisten = useRef(null); const [messages, setMessages] = useState([]); const [messageCount, setMessageCount] = useState(0); const tableRef = useRef(null); useEffect(() => { - listen('log://log', (event) => { - const payload = event.payload as RecordPayload; - const payloadWithTimestamp = { - ...payload, - timestamp: Date.now(), - }; + let cancelled = false; - setMessages((prev) => [payloadWithTimestamp, ...prev]); - setMessageCount((prev) => prev + 1); + const setupListener = async () => { + const unlistenFn = await getCurrentWebview().listen('log://log', (event) => { + const { payload } = event; + const payloadWithTimestamp = { + ...payload, + timestamp: Date.now(), + }; - if (tableRef.current) { - tableRef.current.scrollTop = 0; + setMessages((prev) => [payloadWithTimestamp, ...prev]); + setMessageCount((prev) => prev + 1); + + if (tableRef.current) { + tableRef.current.scrollTop = 0; + } + }); + if (cancelled) { + unlistenFn(); + } else { + unlisten.current = unlistenFn; } - }).then((fn) => { - unlisten.current = fn; - }); + }; + + setupListener(); return () => { + cancelled = true; if (unlisten.current) { unlisten.current(); + unlisten.current = null; } }; }, []); diff --git a/nym-wallet/src/components/Login/LoginPasswordFormWrapper.tsx b/nym-wallet/src/components/Login/LoginPasswordFormWrapper.tsx index 0217b81974..4e4be03187 100644 --- a/nym-wallet/src/components/Login/LoginPasswordFormWrapper.tsx +++ b/nym-wallet/src/components/Login/LoginPasswordFormWrapper.tsx @@ -43,7 +43,12 @@ export const EnhancedPasswordInput: React.FC = ({ const input = inputRef.current.querySelector('input'); if (!input) return undefined; + input.setAttribute('autocomplete', 'new-password'); + input.setAttribute('data-lpignore', 'true'); + input.setAttribute('data-1p-ignore', 'true'); + const handleKeyDown = async (e: KeyboardEvent) => { + if (e.defaultPrevented) return; if (document.activeElement !== input) return; if ((e.metaKey || e.ctrlKey) && e.key === 'a') { @@ -101,7 +106,7 @@ export const EnhancedPasswordInput: React.FC = ({ }; return ( - + = ({ // Fix the event type issue by casting Event to KeyboardEvent const handleKeyDown = async (e: Event) => { const keyEvent = e as KeyboardEvent; + if (keyEvent.defaultPrevented) return; if (document.activeElement !== input) return; if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'a') { @@ -108,7 +109,7 @@ export const EnhancedMnemonicInput: React.FC = ({ }; return ( - + ({ - backdropProps: backDropStyles(theme), - sx: dialogStyles(theme), -}); - -export default { - title: 'Modals/ConfirmationModal', - component: ConfirmationModal, -} as ComponentMeta; - -const Template: ComponentStory = (args) => { - const [open, setOpen] = useState(true); - const theme = useTheme(); - return ( - <> - - setOpen(false)} - onConfirm={() => setOpen(false)} - {...storybookStyles(theme)} - > - Dialog content. - - - ); -}; - -export const withError: ComponentStory = () => { - const [open, setOpen] = useState(true); - const theme = useTheme(); - return ( - <> - - setOpen(false)} - onConfirm={() => setOpen(false)} - {...storybookStyles(theme)} - > - - - - ); -}; - -export const Default = Template.bind({}); -Default.args = { - title: 'Confirmation Modal', - subTitle: '', - fullWidth: true, - confirmButton: 'Confirm', - maxWidth: 'xs', - disabled: false, -}; diff --git a/nym-wallet/src/components/Modals/ModalListItem.tsx b/nym-wallet/src/components/Modals/ModalListItem.tsx index 830c25705e..72f8f215e0 100644 --- a/nym-wallet/src/components/Modals/ModalListItem.tsx +++ b/nym-wallet/src/components/Modals/ModalListItem.tsx @@ -11,22 +11,45 @@ export const ModalListItem: FCWithChildren<{ light?: boolean; value?: React.ReactNode; sxValue?: SxProps; -}> = ({ label, value, hidden, fontWeight, fontSize, divider, sxValue }) => ( + /** row: label and value on one line; stack: label above value (better for long strings) */ + layout?: 'row' | 'stack'; +}> = ({ label, value, hidden, fontWeight, fontSize, divider, sxValue, layout = 'row' }) => ( ); diff --git a/nym-wallet/src/components/Modals/SimpleModal.stories.tsx b/nym-wallet/src/components/Modals/SimpleModal.stories.tsx deleted file mode 100644 index 921e9a3c5a..0000000000 --- a/nym-wallet/src/components/Modals/SimpleModal.stories.tsx +++ /dev/null @@ -1,260 +0,0 @@ -import React from 'react'; -import { ComponentMeta } from '@storybook/react'; -import { Button, Paper, Typography } from '@mui/material'; -import { useTheme, Theme } from '@mui/material/styles'; -import { SimpleModal } from './SimpleModal'; -import { ModalDivider } from './ModalDivider'; -import { backDropStyles, modalStyles } from '../../../.storybook/storiesStyles'; - -const storybookStyles = (theme: Theme) => ({ - backdropProps: backDropStyles(theme), - sx: modalStyles(theme), -}); - -export default { - title: 'Modals/Simple Modal', - component: SimpleModal, -} as ComponentMeta; - -const BasePage: FCWithChildren<{ children: React.ReactElement; handleClick: () => void }> = ({ - children, - handleClick, -}) => ( - <> - -

Lorem ipsum

- - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis sunt - velit elit do minim mollit non duis reprehenderit. Eiusmod dolore adipisicing ex nostrud consectetur culpa - exercitation do. Ad elit esse ipsum aliqua labore irure laborum qui culpa. - - - Occaecat commodo excepteur anim ut officia dolor laboris dolore id occaecat enim qui eiusmod occaecat aliquip ad - tempor. Labore amet laborum magna amet consequat dolor cupidatat in consequat sunt aliquip magna laboris tempor - culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna elit ut - mollit. - - - Labore voluptate elit amet ipsum qui officia duis in et occaecat culpa ex do non labore mollit. Cillum cupidatat - duis ea dolore laboris laboris sunt duis anim consectetur cupidatat nulla ad minim sunt ea. Aliqua amet commodo - est irure sint magna sunt. Pariatur dolore commodo labore quis incididunt proident duis voluptate exercitation - in duis. Occaecat aliqua laboris reprehenderit nostrud est aute pariatur fugiat anim. Dolore sunt cillum ea - aliquip consectetur laborum ipsum qui veniam Lorem consectetur adipisicing velit magna aute. Amet tempor quis - excepteur minim culpa velit Lorem enim ad. - - - Mollit laborum exercitation excepteur laboris adipisicing ipsum veniam cillum mollit voluptate do. Amet et anim - Lorem mollit minim duis cupidatat non. Consectetur sit deserunt nisi nisi non excepteur dolor eiusmod aute aute - irure anim dolore ipsum et veniam. - -
- {children} - -); - -export const Default = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - - const theme = useTheme(); - - return ( - - setOpen(false)} - onOk={async () => setOpen(false)} - header="This is a modal" - subHeader="This is a sub header" - okLabel="Click to continue" - {...storybookStyles(theme)} - > - - Lorem mollit minim duis cupidatat non. Consectetur sit deserunt - - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis. - - - - Occaecat commodo excepteur anim ut officia dolor laboris dolore id occaecat enim qui eius - - - Tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna - elit ut mollit. - - - - ); -}; - -export const NoSubheader = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - - const theme = useTheme(); - - return ( - - setOpen(false)} - onOk={async () => setOpen(false)} - header="This is a modal" - okLabel="Kaplow!" - {...storybookStyles(theme)} - > - - Tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna - elit ut mollit. - - - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis. - - - - ); -}; - -export const hideCloseIcon = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - - const theme = useTheme(); - - return ( - - setOpen(false)} - onOk={async () => setOpen(false)} - header="This is a modal" - okLabel="Kaplow!" - {...storybookStyles(theme)} - > - - Tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna - elit ut mollit. - - - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis. - - - - ); -}; - -export const hideCloseIconAndDisplayErrorIcon = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - - const theme = useTheme(); - - return ( - - setOpen(false)} - onOk={async () => setOpen(false)} - header="This modal announces an error !" - okLabel="Kaplow!" - backdropProps={backDropStyles(theme)} - sx={{ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - ...modalStyles(theme), - }} - headerStyles={{ - width: '100%', - mb: 3, - textAlign: 'center', - color: 'error.main', - }} - subHeaderStyles={{ textAlign: 'center', color: 'text.primary', fontSize: 14, fontWeight: 400 }} - > - - Tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna - elit ut mollit. - - - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis. - - - - ); -}; - -export const withBackButton = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - - const theme = useTheme(); - - return ( - - setOpen(false)} - onOk={async () => setOpen(false)} - header="This is a modal" - okLabel="Primary action" - onBack={() => setOpen(false)} - {...storybookStyles(theme)} - > - - Tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna - elit ut mollit. - - - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis. - - - - ); -}; - -export const withBackButtonAndCustomLabel = () => { - const [open, setOpen] = React.useState(false); - const handleClick = () => setOpen(true); - - const theme = useTheme(); - - return ( - - setOpen(false)} - onOk={async () => setOpen(false)} - header="This is a modal" - okLabel="Primary action" - onBack={() => setOpen(false)} - backLabel="Cancel" - backButtonFullWidth - {...storybookStyles(theme)} - > - - Tempor culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna - elit ut mollit. - - - - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis. - - - - ); -}; diff --git a/nym-wallet/src/components/Modals/SimpleModal.tsx b/nym-wallet/src/components/Modals/SimpleModal.tsx index 7fdeb328b2..485d8c068f 100644 --- a/nym-wallet/src/components/Modals/SimpleModal.tsx +++ b/nym-wallet/src/components/Modals/SimpleModal.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Box, Button, Modal, Stack, SxProps, Typography } from '@mui/material'; +import { alpha } from '@mui/material/styles'; import CloseIcon from '@mui/icons-material/Close'; import ErrorOutline from '@mui/icons-material/ErrorOutline'; import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; @@ -11,9 +12,15 @@ export const SimpleModal: FCWithChildren<{ hideCloseIcon?: boolean; displayErrorIcon?: boolean; displayInfoIcon?: boolean; + /** Center the header title; close control stays top-right. */ + headerCentered?: boolean; headerStyles?: SxProps; subHeaderStyles?: SxProps; buttonFullWidth?: boolean; + /** Tighter padding and typography */ + dense?: boolean; + /** Primary left accent bar */ + accent?: 'none' | 'primary'; onClose?: () => void; onOk?: () => Promise; onBack?: () => void; @@ -31,9 +38,12 @@ export const SimpleModal: FCWithChildren<{ hideCloseIcon, displayErrorIcon, displayInfoIcon, + headerCentered, headerStyles, subHeaderStyles, buttonFullWidth, + dense, + accent = 'none', onClose, okDisabled, onOk, @@ -48,37 +58,103 @@ export const SimpleModal: FCWithChildren<{ backdropProps, }) => ( - `1px solid ${t.palette.nym.nymWallet.modal.border}`, ...modalStyle, ...sx }}> - {displayErrorIcon && } + `1px solid ${t.palette.nym.nymWallet.modal.border}`, + ...modalStyle, + ...(dense ? { p: 3, borderRadius: '12px' } : {}), + ...(accent === 'primary' + ? { + borderLeft: (t) => `4px solid ${t.palette.primary.main}`, + } + : {}), + ...sx, + }} + > + {displayErrorIcon && } {displayInfoIcon && theme.palette.nym.nymWallet.text.blue }} />} - + {typeof header === 'string' ? ( - + {header} ) : ( header )} - {!hideCloseIcon && } + {!hideCloseIcon && ( + + )} - theme.palette.text.secondary} - sx={{ color: (theme) => theme.palette.nym.nymWallet.text.muted, ...subHeaderStyles }} - > - {subHeader} - + {subHeader ? ( + + {subHeader} + + ) : null} {children} {(onOk || onBack) && ( - + {onBack && } {onOk && ( - )} diff --git a/nym-wallet/src/components/Nav.tsx b/nym-wallet/src/components/Nav.tsx index 3036f91682..ee8f6abfd1 100644 --- a/nym-wallet/src/components/Nav.tsx +++ b/nym-wallet/src/components/Nav.tsx @@ -1,88 +1,89 @@ -import React, { useState, useContext } from 'react'; +import React, { useContext, useMemo } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material'; -import { - AccountBalanceWalletOutlined, - ArrowBack, - ArrowForward, - Description, - Settings, - Toll, -} from '@mui/icons-material'; +import { Divider, List, ListItemButton, ListItemIcon, ListItemText, Stack, Typography } from '@mui/material'; +import type { Theme } from '@mui/material/styles'; +import { alpha } from '@mui/material/styles'; +import { AccountBalanceWalletOutlined, Description, Settings, VpnKeyOutlined } from '@mui/icons-material'; +import { safeOpenUrl } from 'src/utils/safeOpenUrl'; import { AppContext } from '../context/main'; import { Delegate, Bonding } from '../svg-icons'; +const activeNavPrimaryColor = (theme: Theme) => + theme.palette.mode === 'dark' ? theme.palette.common.white : theme.palette.grey[900]; + export const Nav = () => { const location = useLocation(); const navigate = useNavigate(); - const { isAdminAddress, handleShowSendModal, handleShowReceiveModal } = useContext(AppContext); + const { isAdminAddress } = useContext(AppContext); - const [routesSchema] = useState([ - { - label: 'Balance', - route: '/balance', - Icon: AccountBalanceWalletOutlined, - onClick: () => navigate('/balance'), - }, - { - label: 'Send', - Icon: ArrowForward, - onClick: handleShowSendModal, - }, - { - label: 'Receive', - Icon: ArrowBack, - onClick: handleShowReceiveModal, - }, - { - label: 'Delegation', - route: '/delegation', - Icon: Delegate, - onClick: () => navigate('/delegation'), - }, - { - label: 'Bonding', - route: '/bonding', - Icon: Bonding, - onClick: () => navigate('/bonding'), - }, - { - label: 'Docs', - route: '/admin', - Icon: Description, - mode: 'dev', - onClick: () => navigate('/docs'), - }, - { - label: 'Admin', - route: '/admin', - Icon: Settings, - mode: 'admin', - onClick: () => navigate('/admin'), - }, - { - label: 'Buy', - route: '/buy', - Icon: Toll, - onClick: () => navigate('/buy'), - }, - ]); + const routesSchema = useMemo( + () => [ + { + label: 'Balance', + description: 'Portfolio', + route: '/balance', + Icon: AccountBalanceWalletOutlined, + onClick: () => navigate('/balance'), + }, + { + label: 'Delegation', + description: 'Stake and manage rewards', + route: '/delegation', + Icon: Delegate, + onClick: () => navigate('/delegation'), + }, + { + label: 'Bonding', + description: 'Run operator workflows', + route: '/bonding', + Icon: Bonding, + onClick: () => navigate('/bonding'), + }, + { + label: 'Docs', + description: 'Internal wallet notes', + route: '/docs', + Icon: Description, + mode: 'dev', + onClick: () => navigate('/docs'), + }, + { + label: 'Admin', + description: 'Network management', + route: '/admin', + Icon: Settings, + mode: 'admin', + onClick: () => navigate('/admin'), + }, + ], + [navigate], + ); + + const ecosystemLinks = useMemo( + () => [ + { + label: 'NymVPN', + description: 'Plans and subscribe', + href: 'https://nym.com/pricing', + Icon: VpnKeyOutlined, + }, + ], + [], + ); return ( -
+ + + Navigation + {routesSchema @@ -99,47 +100,137 @@ export const Nav = () => { return false; } }) - .map(({ Icon, onClick, label, route }) => ( - theme.palette.nym.nymWallet.hover.background }, - }} - > - { + const isActive = route ? location.pathname.startsWith(route) : false; + + return ( + - - - (theme.palette.mode === 'light' ? 600 : 500), + px: 2, + py: 2, + borderRadius: 3, + alignItems: 'flex-start', + border: (theme) => { + if (isActive) { + return `1px solid ${theme.palette.primary.main}`; + } + if (theme.palette.mode === 'dark') { + return '1px solid rgba(255,255,255,0.06)'; + } + return `1px solid ${alpha(theme.palette.common.black, 0.08)}`; + }, + backgroundColor: (theme) => { + if (isActive) { + return `${theme.palette.primary.main}12`; + } + + return theme.palette.mode === 'dark' + ? 'rgba(255,255,255,0.02)' + : alpha(theme.palette.common.black, 0.02); + }, + '&:hover': { + backgroundColor: (theme) => + isActive ? `${theme.palette.primary.main}18` : theme.palette.nym.nymWallet.hover.background, }, }} - primary={label} - /> - - ))} + > + + + + (isActive ? activeNavPrimaryColor(theme) : theme.palette.text.primary), + }, + '& .MuiListItemText-secondary': { + mt: 0.5, + fontSize: 12, + lineHeight: 1.35, + color: 'text.secondary', + }, + }} + primary={label} + secondary={description} + /> + + ); + })} + + + Ecosystem + + {ecosystemLinks.map(({ Icon, label, description, href }) => ( + { + safeOpenUrl(href).catch(() => { + /* opener unavailable or user cancelled */ + }); + }} + sx={{ + px: 2, + py: 2, + borderRadius: 3, + alignItems: 'flex-start', + border: (theme) => + theme.palette.mode === 'dark' + ? '1px solid rgba(255,255,255,0.06)' + : `1px solid ${alpha(theme.palette.common.black, 0.08)}`, + backgroundColor: (theme) => + theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.02)' : alpha(theme.palette.common.black, 0.02), + '&:hover': { + backgroundColor: (theme) => theme.palette.nym.nymWallet.hover.background, + }, + }} + > + + + + + + ))} -
+
); }; diff --git a/nym-wallet/src/components/NetworkSelector.stories.tsx b/nym-wallet/src/components/NetworkSelector.stories.tsx deleted file mode 100644 index 52cd74c6cc..0000000000 --- a/nym-wallet/src/components/NetworkSelector.stories.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import * as React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Box } from '@mui/material'; -import { MockMainContextProvider } from '../context/mocks/main'; -import { NetworkSelector } from './NetworkSelector'; - -export default { - title: 'Wallet / Network Selector', - component: NetworkSelector, -} as ComponentMeta; - -const Template: ComponentStory = () => ( - - - - - -); - -export const Default = Template.bind({}); diff --git a/nym-wallet/src/components/NetworkSelector.tsx b/nym-wallet/src/components/NetworkSelector.tsx index 5df0d7ec74..efd7493caf 100644 --- a/nym-wallet/src/components/NetworkSelector.tsx +++ b/nym-wallet/src/components/NetworkSelector.tsx @@ -4,6 +4,7 @@ import { ArrowDropDown, Check } from '@mui/icons-material'; import { Network } from 'src/types'; import { AppContext } from '../context/main'; import { config } from '../config'; +import { headerControlPillSx } from './headerControlPillSx'; const networks: { networkName: Network; name: string }[] = [ { networkName: 'MAINNET', name: 'Nym Mainnet' }, @@ -59,10 +60,10 @@ export const NetworkSelector = () => { diff --git a/nym-wallet/src/components/NymCard.tsx b/nym-wallet/src/components/NymCard.tsx index ae3d84fe23..b6da460be6 100644 --- a/nym-wallet/src/components/NymCard.tsx +++ b/nym-wallet/src/components/NymCard.tsx @@ -11,34 +11,53 @@ const CardContentNoPadding = styled(CardContent)(() => ({ })); export const NymCard: FCWithChildren<{ - title: string | React.ReactElement; - subheader?: string | React.ReactChild; + title?: string | React.ReactElement; + subheader?: React.ReactNode; Action?: React.ReactNode; Icon?: React.ReactNode; noPadding?: boolean; borderless?: boolean; + /** Omit the card header row (use for fully custom headers inside children). */ + hideHeader?: boolean; dataTestid?: string; sx?: SxProps; sxTitle?: SxProps; children?: React.ReactNode; -}> = ({ title, subheader, Action, Icon, noPadding, borderless, children, dataTestid, sx, sxTitle }) => ( - - theme.palette.text.primary, - '& .MuiCardHeader-title h5': { fontSize: '1.25rem' }, - }} - title={} - subheader={subheader} - data-testid={dataTestid || title} - subheaderTypographyProps={{ variant: 'subtitle1' }} - action={Action} - /> +}> = ({ title, subheader, Action, Icon, noPadding, borderless, hideHeader, children, dataTestid, sx, sxTitle }) => ( + <Card + variant="outlined" + data-testid={hideHeader ? dataTestid : undefined} + sx={{ + overflow: 'hidden', + borderRadius: 4, + borderColor: 'divider', + backgroundImage: 'none', + ...(borderless && { border: 'none', boxShadow: 'none' }), + ...sx, + }} + > + {!hideHeader && title !== undefined && ( + <CardHeader + sx={{ + p: 3, + color: (theme: Theme) => theme.palette.text.primary, + '& .MuiCardHeader-title h5': { fontSize: '1.25rem' }, + '& .MuiCardHeader-action': { + alignSelf: 'center', + m: 0, + }, + }} + title={<Title title={title} Icon={Icon} sx={sxTitle} />} + subheader={subheader} + data-testid={dataTestid || (typeof title === 'string' ? title : 'nym-card')} + subheaderTypographyProps={{ variant: 'subtitle1' }} + action={Action} + /> + )} {noPadding ? ( <CardContentNoPadding>{children}</CardContentNoPadding> ) : ( - <CardContent sx={{ p: 3, paddingTop: 0 }}>{children}</CardContent> + <CardContent sx={{ p: 3, paddingTop: hideHeader ? 3 : 0 }}>{children}</CardContent> )} </Card> ); diff --git a/nym-wallet/src/components/Receive/ReceiveModal.tsx b/nym-wallet/src/components/Receive/ReceiveModal.tsx index b7373cadc0..01836aeed0 100644 --- a/nym-wallet/src/components/Receive/ReceiveModal.tsx +++ b/nym-wallet/src/components/Receive/ReceiveModal.tsx @@ -1,54 +1,48 @@ import React, { useContext } from 'react'; import { AppContext } from 'src/context'; -import { Box, Stack, SxProps, Typography, alpha, useTheme } from '@mui/material'; -import QRCode from 'qrcode.react'; -import { ClientAddress } from '@nymproject/react/client-address/ClientAddress'; +import { Box, Stack, Typography, alpha, useTheme } from '@mui/material'; +import QrCode2Icon from '@mui/icons-material/QrCode2'; +import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'; +import { QrCodeReact } from 'src/utils/qrCodeReact'; import { SimpleModal } from '../Modals/SimpleModal'; -export const ReceiveModal = ({ - onClose, - sx, - backdropProps, -}: { - onClose: () => void; - sx?: SxProps; - backdropProps?: object; -}) => { +export const ReceiveModal = ({ onClose }: { onClose: () => void }) => { const { clientDetails } = useContext(AppContext); const theme = useTheme(); const isLightMode = theme.palette.mode === 'light'; const highlightColor = theme.palette.nym.highlight; const darkBgColor = theme.palette.background.default; + const address = clientDetails?.client_address?.trim() ?? ''; return ( <SimpleModal - header="Receive" + header="Receive NYM" + subHeader="Share your address or scan the QR code to receive NYM from another wallet." + headerCentered open onClose={onClose} okLabel="" sx={{ - ...sx, '& .MuiPaper-root': { overflow: 'hidden', borderRadius: '20px', - boxShadow: '0 12px 48px rgba(0, 0, 0, 0.15)', + boxShadow: theme.palette.nym.nymWallet.shadows.strong, maxWidth: '480px', }, }} - backdropProps={backdropProps} - subHeaderStyles={{ mb: 0, px: 3, pt: 2 }} + subHeaderStyles={{ mb: 0, px: 3, pt: 0.5 }} > <Stack - gap={4} + gap={3} sx={{ position: 'relative', px: 3, pb: 3, - pt: 1, + pt: 0, }} > - <Box> + <Box sx={{ width: '100%' }}> <Typography variant="caption" sx={{ @@ -56,6 +50,7 @@ export const ReceiveModal = ({ color: 'text.secondary', fontWeight: 600, display: 'block', + textAlign: 'center', }} > Your address @@ -64,23 +59,43 @@ export const ReceiveModal = ({ <Box sx={{ p: 2, - bgcolor: alpha(theme.palette.primary.main, 0.04), + bgcolor: alpha(theme.palette.primary.main, 0.06), borderRadius: '12px', - border: `1px solid ${alpha(theme.palette.primary.main, 0.1)}`, + border: `1px solid ${alpha(theme.palette.primary.main, 0.18)}`, position: 'relative', }} > - {clientDetails?.client_address && ( + {address ? ( <Box sx={{ - fontSize: '0.9rem', - fontFamily: 'monospace', - letterSpacing: '0.5px', - wordBreak: 'break-all', + display: 'flex', + alignItems: 'flex-start', + justifyContent: 'space-between', + gap: 1.5, + width: '100%', }} > - <ClientAddress address={clientDetails?.client_address} withCopy showEntireAddress /> + <Typography + component="span" + sx={{ + flex: 1, + minWidth: 0, + fontSize: '0.9rem', + fontFamily: 'monospace', + letterSpacing: '0.5px', + wordBreak: 'break-all', + color: 'text.primary', + textAlign: 'left', + }} + > + {address} + </Typography> + <CopyToClipboard value={address} sx={{ flexShrink: 0, mt: 0.25 }} /> </Box> + ) : ( + <Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center' }}> + No client address available. Sign in again if this persists. + </Typography> )} </Box> </Box> @@ -92,12 +107,22 @@ export const ReceiveModal = ({ display: 'flex', flexDirection: 'column', alignItems: 'center', - bgcolor: isLightMode ? alpha(highlightColor, 0.06) : alpha(darkBgColor, 0.7), + bgcolor: isLightMode ? alpha(highlightColor, 0.06) : alpha(theme.palette.background.paper, 0.5), borderRadius: '16px', - py: 4, + py: 3, px: 2, + border: `1px solid ${alpha(highlightColor, isLightMode ? 0.2 : 0.12)}`, }} > + <Stack direction="row" alignItems="center" gap={1} sx={{ mb: 1.5 }}> + <QrCode2Icon sx={{ color: 'primary.main', fontSize: 22 }} /> + <Typography variant="subtitle2" fontWeight={600} color="text.primary"> + QR code + </Typography> + </Stack> + <Typography variant="caption" color="text.secondary" sx={{ mb: 2, textAlign: 'center', maxWidth: 320 }}> + Share this address only with people you trust. + </Typography> <Box sx={{ position: 'relative', @@ -114,7 +139,7 @@ export const ReceiveModal = ({ width: '100%', height: '100%', borderRadius: '16px', - background: `radial-gradient(circle, ${alpha(highlightColor, 0.15)} 0%, transparent 70%)`, + background: `radial-gradient(circle, ${alpha(highlightColor, 0.18)} 0%, transparent 70%)`, }} /> @@ -124,9 +149,9 @@ export const ReceiveModal = ({ justifyContent: 'center', alignItems: 'center', p: 3, - bgcolor: isLightMode ? 'white' : alpha(darkBgColor, 0.7), + bgcolor: isLightMode ? 'white' : alpha(darkBgColor, 0.85), borderRadius: '16px', - border: `2px solid ${isLightMode ? highlightColor : theme.palette.nym.nymWallet.modal.border}`, + border: `2px solid ${isLightMode ? highlightColor : alpha(highlightColor, 0.35)}`, boxShadow: `0 10px 32px ${alpha(theme.palette.common.black, 0.1)}`, transition: 'transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out', '&:hover': { @@ -135,22 +160,21 @@ export const ReceiveModal = ({ }, }} > - {clientDetails && ( - <QRCode + {address ? ( + <QrCodeReact + renderAs="svg" data-testid="qr-code" - value={clientDetails?.client_address} + value={address} size={200} level="H" includeMargin bgColor={isLightMode ? '#FFFFFF' : theme.palette.background.paper} fgColor={isLightMode ? '#000000' : highlightColor} - imageSettings={{ - src: '', - excavate: true, - width: 32, - height: 32, - }} /> + ) : ( + <Typography variant="caption" color="text.secondary"> + QR unavailable without an address + </Typography> )} </Box> </Box> @@ -158,10 +182,11 @@ export const ReceiveModal = ({ <Typography variant="body2" sx={{ - mt: 3, + mt: 2.5, color: 'text.secondary', textAlign: 'center', - maxWidth: '80%', + maxWidth: '90%', + lineHeight: 1.5, }} > Scan this QR code with a compatible wallet to receive NYM tokens diff --git a/nym-wallet/src/components/Receive/index.tsx b/nym-wallet/src/components/Receive/index.tsx index 7c32156c74..9f292d0554 100644 --- a/nym-wallet/src/components/Receive/index.tsx +++ b/nym-wallet/src/components/Receive/index.tsx @@ -2,10 +2,10 @@ import React, { useContext } from 'react'; import { AppContext } from 'src/context'; import { ReceiveModal } from './ReceiveModal'; -export const Receive = ({ hasStorybookStyles }: { hasStorybookStyles?: {} }) => { - const { showReceiveModal, handleShowReceiveModal } = useContext(AppContext); +export const Receive = () => { + const { showReceiveModal, handleCloseReceiveModal } = useContext(AppContext); - if (showReceiveModal) return <ReceiveModal onClose={handleShowReceiveModal} {...hasStorybookStyles} />; + if (showReceiveModal) return <ReceiveModal onClose={handleCloseReceiveModal} />; return null; }; diff --git a/nym-wallet/src/components/Rewards/RedeemModal.stories.tsx b/nym-wallet/src/components/Rewards/RedeemModal.stories.tsx deleted file mode 100644 index 9d24f95733..0000000000 --- a/nym-wallet/src/components/Rewards/RedeemModal.stories.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import React from 'react'; -import { ComponentMeta } from '@storybook/react'; -import { Button, Paper } from '@mui/material'; -import { useTheme, Theme } from '@mui/material/styles'; -import { RedeemModal } from './RedeemModal'; -import { backDropStyles, modalStyles } from '../../../.storybook/storiesStyles'; - -const storybookStyles = (theme: Theme) => ({ - backdropProps: backDropStyles(theme), - sx: modalStyles(theme), -}); - -export default { - title: 'Rewards/Components/Redeem Modals', - component: RedeemModal, -} as ComponentMeta<typeof RedeemModal>; - -const Content: FCWithChildren<{ - setOpen: (value: boolean) => void; -}> = ({ setOpen }) => ( - <Paper elevation={0} sx={{ px: 4, pt: 2, pb: 4 }}> - <h2>Lorem ipsum</h2> - <Button variant="contained" onClick={() => setOpen(true)}> - Show modal - </Button> - <p> - Veniam dolor laborum labore sit reprehenderit enim mollit magna nulla adipisicing fugiat. Est ex irure quis sunt - velit elit do minim mollit non duis reprehenderit. Eiusmod dolore adipisicing ex nostrud consectetur culpa - exercitation do. Ad elit esse ipsum aliqua labore irure laborum qui culpa. - </p> - <p> - Occaecat commodo excepteur anim ut officia dolor laboris dolore id occaecat enim qui eiusmod occaecat aliquip ad - tempor. Labore amet laborum magna amet consequat dolor cupidatat in consequat sunt aliquip magna laboris tempor - culpa est magna. Sit tempor cillum culpa sint ipsum nostrud ullamco voluptate exercitation dolore magna elit ut - mollit. - </p> - <p> - Labore voluptate elit amet ipsum qui officia duis in et occaecat culpa ex do non labore mollit. Cillum cupidatat - duis ea dolore laboris laboris sunt duis anim consectetur cupidatat nulla ad minim sunt ea. Aliqua amet commodo - est irure sint magna sunt. Pariatur dolore commodo labore quis incididunt proident duis voluptate exercitation in - duis. Occaecat aliqua laboris reprehenderit nostrud est aute pariatur fugiat anim. Dolore sunt cillum ea aliquip - consectetur laborum ipsum qui veniam Lorem consectetur adipisicing velit magna aute. Amet tempor quis excepteur - minim culpa velit Lorem enim ad. - </p> - <p> - Mollit laborum exercitation excepteur laboris adipisicing ipsum veniam cillum mollit voluptate do. Amet et anim - Lorem mollit minim duis cupidatat non. Consectetur sit deserunt nisi nisi non excepteur dolor eiusmod aute aute - irure anim dolore ipsum et veniam. - </p> - </Paper> -); - -export const RedeemAllRewards = () => { - const [open, setOpen] = React.useState<boolean>(false); - const theme = useTheme(); - return ( - <> - <Content setOpen={setOpen} /> - <RedeemModal - open={open} - onClose={() => setOpen(false)} - onOk={async () => setOpen(false)} - message="Redeem all rewards" - denom="nym" - mixId={1234} - identityKey="D88RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujaaa" - amount={425.65843} - {...storybookStyles(theme)} - usesVestingTokens={false} - /> - </> - ); -}; - -export const RedeemRewardForMixnode = () => { - const [open, setOpen] = React.useState<boolean>(false); - const theme = useTheme(); - return ( - <> - <Content setOpen={setOpen} /> - <RedeemModal - open={open} - onClose={() => setOpen(false)} - onOk={async () => setOpen(false)} - message="Claim rewards" - denom="nym" - mixId={1234} - identityKey="D88RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujaaa" - amount={425.65843} - {...storybookStyles(theme)} - usesVestingTokens={false} - /> - </> - ); -}; - -export const FeeIsMoreThanAllRewards = () => { - const [open, setOpen] = React.useState<boolean>(false); - const theme = useTheme(); - return ( - <> - <Content setOpen={setOpen} /> - <RedeemModal - open={open} - onClose={() => setOpen(false)} - onOk={() => setOpen(false)} - message="Redeem all rewards" - denom="nym" - mixId={1234} - identityKey="D88RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujaaa" - amount={0.001} - {...storybookStyles(theme)} - usesVestingTokens={false} - /> - </> - ); -}; - -export const FeeIsMoreThanMixnodeReward = () => { - const [open, setOpen] = React.useState<boolean>(false); - const theme = useTheme(); - return ( - <> - <Content setOpen={setOpen} /> - <RedeemModal - open={open} - onClose={() => setOpen(false)} - onOk={async () => setOpen(false)} - mixId={1234} - identityKey="D88RfeY8DttMD3CQKoayV6mss5a5FC3RoH75Kmcujaaa" - message="Claim rewards" - denom="nym" - amount={0.001} - {...storybookStyles(theme)} - usesVestingTokens={false} - /> - </> - ); -}; diff --git a/nym-wallet/src/components/Rewards/RewardsSummary.stories.tsx b/nym-wallet/src/components/Rewards/RewardsSummary.stories.tsx deleted file mode 100644 index d824eb33d6..0000000000 --- a/nym-wallet/src/components/Rewards/RewardsSummary.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { ComponentMeta } from '@storybook/react'; - -import { Paper } from '@mui/material'; -import { RewardsSummary } from './RewardsSummary'; - -export default { - title: 'Rewards/Components/Rewards Summary', - component: RewardsSummary, -} as ComponentMeta<typeof RewardsSummary>; - -export const Default = () => ( - <Paper elevation={0} sx={{ px: 4, py: 2 }}> - <RewardsSummary totalDelegation="860.123 NYM" totalRewards="4.86723 NYM" /> - </Paper> -); - -export const Empty = () => ( - <Paper elevation={0} sx={{ px: 4, py: 2 }}> - <RewardsSummary /> - </Paper> -); - -export const Loading = () => ( - <Paper elevation={0} sx={{ px: 4, py: 2 }}> - <RewardsSummary isLoading /> - </Paper> -); diff --git a/nym-wallet/src/components/Send/SendDetails.stories.tsx b/nym-wallet/src/components/Send/SendDetails.stories.tsx deleted file mode 100644 index ced7ee44ce..0000000000 --- a/nym-wallet/src/components/Send/SendDetails.stories.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; -import { ComponentMeta } from '@storybook/react'; -import { useTheme, Theme } from '@mui/material/styles'; -import { MockMainContextProvider } from 'src/context/mocks/main'; -import { SendDetailsModal } from './SendDetailsModal'; -import { SendSuccessModal } from './SendSuccessModal'; -import { SendErrorModal } from './SendErrorModal'; -import { SendInputModal } from './SendInputModal'; -import { Send } from '.'; -import { backDropStyles, modalStyles, dialogStyles } from '../../../.storybook/storiesStyles'; - -const storybookStylesModal = (theme: Theme) => ({ - backdropProps: backDropStyles(theme), - sx: modalStyles(theme), -}); - -const storybookStylesDialog = (theme: Theme) => ({ - backdropProps: backDropStyles(theme), - sx: dialogStyles(theme), -}); - -export default { - title: 'Send/Components', - component: SendDetailsModal, -} as ComponentMeta<typeof SendDetailsModal>; - -export const SendInput = () => { - const theme = useTheme(); - return ( - <SendInputModal - toAddress="" - fromAddress="nymt1w8qp7zsxggvtxhpqpt6e329j42wtv07dm5ts8u" - denom="nym" - onNext={() => {}} - onClose={() => {}} - onAddressChange={() => {}} - onAmountChange={() => {}} - onUserFeesChange={() => {}} - onMemoChange={() => {}} - setShowMore={() => {}} - {...storybookStylesModal(theme)} - /> - ); -}; - -export const SendDetails = () => { - const theme = useTheme(); - return ( - <SendDetailsModal - fromAddress="nymt1w8qp7zsxggvtxhpqpt6e329j42wtv07dm5ts8u" - toAddress="nymt1w8qp7zsxggvtxhpqpt6e329j42wtv07dm5ts8u" - fee={{ amount: { amount: '0.01', denom: 'nym' }, fee: { Auto: null } }} - denom="nym" - amount={{ amount: '100', denom: 'nym' }} - onPrev={() => {}} - onSend={() => {}} - onClose={() => {}} - {...storybookStylesModal(theme)} - /> - ); -}; - -export const SendSuccess = () => { - const theme = useTheme(); - return ( - <SendSuccessModal - txDetails={{ amount: '100 NYM', txUrl: 'dummtUrl.com' }} - onClose={() => {}} - {...storybookStylesDialog(theme)} - /> - ); -}; - -export const SendError = () => { - const theme = useTheme(); - return <SendErrorModal onClose={() => {}} {...storybookStylesModal(theme)} />; -}; - -export const SendFlow = () => { - const theme = useTheme(); - return ( - <MockMainContextProvider> - <Send hasStorybookStyles={{ backdropProps: { ...backDropStyles(theme) }, sx: modalStyles(theme) }} /> - </MockMainContextProvider> - ); -}; diff --git a/nym-wallet/src/components/Send/SendInputModal.tsx b/nym-wallet/src/components/Send/SendInputModal.tsx index f561675cae..e35570e40b 100644 --- a/nym-wallet/src/components/Send/SendInputModal.tsx +++ b/nym-wallet/src/components/Send/SendInputModal.tsx @@ -1,7 +1,9 @@ import React, { useEffect, useState } from 'react'; -import { Stack, Typography, SxProps, FormControlLabel, Checkbox, Alert } from '@mui/material'; +import { Box, Stack, Typography, SxProps, FormControlLabel, Checkbox, Alert } from '@mui/material'; +import { alpha, useTheme } from '@mui/material/styles'; import Big from 'big.js'; import { CurrencyDenom, DecCoin, isValidRawCoin } from '@nymproject/types'; +import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard'; import { validateAmount } from 'src/utils'; import { SimpleModal } from '../Modals/SimpleModal'; import { ModalListItem } from '../Modals/ModalListItem'; @@ -12,6 +14,20 @@ const maxUserFees = '10.0'; const minUserFees = '0.000001'; // aka 1 unym const MIN_AMOUNT_TO_SEND = '0.000001'; // Adjust this as needed +const recipientHelperText = ( + recipientTouched: boolean, + toAddress: string, + addressIsValid: boolean, +): string | undefined => { + if (recipientTouched && !toAddress.trim()) { + return 'Recipient address is required'; + } + if (toAddress !== '' && !addressIsValid) { + return 'Invalid NYM address. Must start with n1 and be exactly 40 characters long.'; + } + return undefined; +}; + // NYM address validation function const validateNymAddress = (address: string): boolean => { if (!address) return false; @@ -69,6 +85,8 @@ export const SendInputModal = ({ const [addressIsValid, setAddressIsValid] = useState(false); const [errorAmount, setErrorAmount] = useState<string | undefined>(); const [errorFee, setErrorFee] = useState<string | undefined>(); + const [recipientTouched, setRecipientTouched] = useState(false); + const theme = useTheme(); // Calculate noAccount at the component root level instead of using useEffect const noAccount = !balance || balance === '0' || parseFloat(balance) === 0; @@ -85,8 +103,9 @@ export const SendInputModal = ({ return newValidatedValue; } - if (!value.amount) { + if (!value?.amount || String(value.amount).trim() === '') { newValidatedValue = false; + errorAmountMessage = 'Amount is required'; } else { // Skip validation for partial decimal inputs during typing if (value.amount === '.' || value.amount.endsWith('.')) { @@ -109,7 +128,7 @@ export const SendInputModal = ({ if (amountBig.gt(balanceBig)) { newValidatedValue = false; - errorAmountMessage = `Make sure you have sufficient funds. Available: ${balance} ${denom?.toUpperCase()}`; + errorAmountMessage = `Amount exceeds your available balance. Available: ${balance} ${denom?.toUpperCase()}`; } } catch (err) { if (!/^\d*\.?\d*$/.test(value.amount)) { @@ -171,8 +190,9 @@ export const SendInputModal = ({ }; useEffect(() => { - if (amount) validateSendAmount(amount); - }, [amount, balance, noAccount]); + const empty: DecCoin = { amount: '', denom: denom ?? 'nym' }; + validateSendAmount(amount ?? empty); + }, [amount, balance, noAccount, denom]); // Effect to validate address whenever it changes useEffect(() => { @@ -198,35 +218,95 @@ export const SendInputModal = ({ return ( <SimpleModal header="Send" + subHeader="Enter the recipient address and amount to send NYM." + headerCentered open onClose={onClose} okLabel="Next" onOk={async () => onNext()} okDisabled={!isValid || !memoIsValid || !feeAmountIsValid || !addressIsValid || noAccount} - sx={sx} + subHeaderStyles={{ mb: 0, px: 3, pt: 0.5 }} + sx={{ + maxWidth: '480px', + width: '100%', + borderRadius: '20px', + overflow: 'hidden', + boxShadow: theme.palette.nym.nymWallet.shadows.strong, + ...sx, + }} backdropProps={backdropProps} > - {noAccount && ( - <Alert severity="warning" sx={{ mb: 3 }}> - To start staking, sending or operating the on the NYM network, you first need to get native NYM tokens. - </Alert> - )} - - <Stack gap={3}> - <ModalListItem label="Your address" value={fromAddress} fontWeight="light" /> + <Stack gap={3} sx={{ px: 3, pb: 3, pt: 0 }}> + {noAccount && ( + <Alert severity="warning"> + To start staking, sending or operating on the NYM network, you first need to get native NYM tokens. + </Alert> + )} + <Box sx={{ width: '100%' }}> + <Typography + variant="caption" + sx={{ + mb: 1.5, + color: 'text.secondary', + fontWeight: 600, + display: 'block', + textAlign: 'center', + }} + > + Your address + </Typography> + <Box + sx={{ + p: 2, + bgcolor: alpha(theme.palette.primary.main, 0.06), + borderRadius: '12px', + border: `1px solid ${alpha(theme.palette.primary.main, 0.18)}`, + }} + > + {fromAddress ? ( + <Box + sx={{ + display: 'flex', + alignItems: 'flex-start', + justifyContent: 'space-between', + gap: 1.5, + width: '100%', + }} + > + <Typography + component="span" + sx={{ + flex: 1, + minWidth: 0, + fontSize: '0.9rem', + fontFamily: 'monospace', + letterSpacing: '0.5px', + wordBreak: 'break-all', + color: 'text.primary', + textAlign: 'left', + }} + > + {fromAddress} + </Typography> + <CopyToClipboard value={fromAddress} sx={{ flexShrink: 0, mt: 0.25 }} /> + </Box> + ) : ( + <Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center' }}> + - + </Typography> + )} + </Box> + </Box> {/* Recipient address field with paste button */} <TextFieldWithPaste label="Recipient address" fullWidth onChange={(e) => onAddressChange(e.target.value)} + onBlur={() => setRecipientTouched(true)} value={toAddress} - error={toAddress !== '' && !addressIsValid} - helperText={ - toAddress !== '' && !addressIsValid - ? 'Invalid NYM address. Must start with n1 and be exactly 40 characters long.' - : undefined - } + error={(recipientTouched && !toAddress.trim()) || (toAddress !== '' && !addressIsValid)} + helperText={recipientHelperText(recipientTouched, toAddress, addressIsValid)} InputLabelProps={{ shrink: true }} onPasteValue={onAddressChange} /> @@ -263,36 +343,40 @@ export const SendInputModal = ({ <Typography fontSize="smaller" sx={{ color: 'error.main' }}> {error} </Typography> - </Stack> - <Stack gap={0.5} sx={{ mt: 1 }}> - <ModalListItem label="Account balance" value={balance ? balance.toUpperCase() : '0'} divider fontWeight={600} /> - <Typography fontSize="smaller" sx={{ color: 'text.primary' }}> - Est. fee for this transaction will be shown on the next page - </Typography> - </Stack> - - <FormControlLabel - control={<Checkbox onChange={() => setShowMore(!showMore)} checked={showMore} />} - label="More options" - sx={{ mt: 2 }} - /> - - {showMore && ( - <Stack mt={2} mb={3}> - <CurrencyFormFieldWithPaste - label="Fee" - onChanged={(v) => { - onUserFeesChange(v); - validateUserFees(v); - }} - initialValue={userFees?.amount} - fullWidth - denom={denom} - validationError={errorFee} + <Stack gap={0.5}> + <ModalListItem + label="Account balance" + value={balance ? balance.toUpperCase() : '0'} + divider + fontWeight={600} /> + <Typography fontSize="smaller" sx={{ color: 'text.primary' }}> + Est. fee for this transaction will be shown on the next page + </Typography> </Stack> - )} + + <FormControlLabel + control={<Checkbox onChange={() => setShowMore(!showMore)} checked={showMore} />} + label="More options" + /> + + {showMore && ( + <Stack spacing={2}> + <CurrencyFormFieldWithPaste + label="Fee" + onChanged={(v) => { + onUserFeesChange(v); + validateUserFees(v); + }} + initialValue={userFees?.amount} + fullWidth + denom={denom} + validationError={errorFee} + /> + </Stack> + )} + </Stack> </SimpleModal> ); }; diff --git a/nym-wallet/src/components/Send/SendModal.tsx b/nym-wallet/src/components/Send/SendModal.tsx index 77aeb7dd6b..f63280ac3b 100644 --- a/nym-wallet/src/components/Send/SendModal.tsx +++ b/nym-wallet/src/components/Send/SendModal.tsx @@ -12,7 +12,7 @@ import { SendInputModal } from './SendInputModal'; import { SendSuccessModal } from './SendSuccessModal'; import { TTransactionDetails } from './types'; -export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void; hasStorybookStyles?: {} }) => { +export const SendModal = ({ onClose }: { onClose: () => void }) => { const [toAddress, setToAddress] = useState<string>(''); const [amount, setAmount] = useState<DecCoin>(); const [modal, setModal] = useState<'send' | 'send details'>('send'); @@ -108,7 +108,6 @@ export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void onSend={handleSend} denom={clientDetails?.display_mix_denom || 'nym'} memo={memo} - {...hasStorybookStyles} /> ); @@ -130,7 +129,6 @@ export const SendModal = ({ onClose, hasStorybookStyles }: { onClose: () => void onUserFeesChange={(value) => setUserFees(value)} onMemoChange={(value) => setMemo(value)} setShowMore={setShowMoreOptions} - {...hasStorybookStyles} /> ); }; diff --git a/nym-wallet/src/components/Send/index.tsx b/nym-wallet/src/components/Send/index.tsx index adba37b7d9..8c4df33f5f 100644 --- a/nym-wallet/src/components/Send/index.tsx +++ b/nym-wallet/src/components/Send/index.tsx @@ -2,10 +2,10 @@ import React, { useContext } from 'react'; import { AppContext } from 'src/context'; import { SendModal } from './SendModal'; -export const Send = ({ hasStorybookStyles }: { hasStorybookStyles?: {} }) => { - const { showSendModal, handleShowSendModal } = useContext(AppContext); +export const Send = () => { + const { showSendModal, handleCloseSendModal } = useContext(AppContext); - if (showSendModal) return <SendModal onClose={handleShowSendModal} hasStorybookStyles={hasStorybookStyles} />; + if (showSendModal) return <SendModal onClose={handleCloseSendModal} />; return null; }; diff --git a/nym-wallet/src/components/Settings/AppVersion.tsx b/nym-wallet/src/components/Settings/AppVersion.tsx index 28e4dcc824..6aaedfb859 100644 --- a/nym-wallet/src/components/Settings/AppVersion.tsx +++ b/nym-wallet/src/components/Settings/AppVersion.tsx @@ -1,6 +1,7 @@ import React, { useContext, useEffect, useState } from 'react'; import { Button, Stack, Typography } from '@mui/material'; import { check } from '@tauri-apps/plugin-updater'; +import { relaunch } from '@tauri-apps/plugin-process'; import { AppContext } from '../../context'; import { checkVersion } from '../../requests'; import { Console } from '../../utils/console'; @@ -26,25 +27,30 @@ const AppVersion = () => { const updateHandler = async () => { try { - // despite the name, this will spawn an external native window with - // an embedded "download and update the Wallet" flow - check(); + const update = await check(); + if (update) { + await update.downloadAndInstall(); + await relaunch(); + } } catch (e) { Console.error(e); } }; return ( - <Stack direction="column" alignItems="flex-end" gap={1}> - <Stack direction="row" gap={1} alignItems="center"> - <Typography variant="caption" sx={{ color: 'nym.text.muted' }}> - Installed version + <Stack direction="column" alignItems="flex-end" gap={1} sx={{ textAlign: 'right' }}> + <Stack direction="row" gap={1} alignItems="center" flexWrap="wrap" justifyContent="flex-end"> + <Typography variant="caption" sx={{ color: 'text.secondary' }}> + Installed </Typography> - <Typography>{`Nym Wallet v${appVersion}`}</Typography> + <Typography fontWeight={600}>{`Nym Wallet v${appVersion}`}</Typography> </Stack> + <Button variant="outlined" size="small" onClick={() => updateCheck()}> + Check for updates + </Button> {updateAvailable && ( - <Button variant="text" onClick={() => updateHandler()}> - Update available + <Button variant="contained" size="small" onClick={() => updateHandler()}> + Download update </Button> )} </Stack> diff --git a/nym-wallet/src/components/TauriLinkWrapper.tsx b/nym-wallet/src/components/TauriLinkWrapper.tsx index edf2e20839..34dc6e9a10 100644 --- a/nym-wallet/src/components/TauriLinkWrapper.tsx +++ b/nym-wallet/src/components/TauriLinkWrapper.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Link, LinkProps } from '@nymproject/react/link/Link'; -import { openUrl } from '@tauri-apps/plugin-opener'; +import { safeOpenUrl } from 'src/utils/safeOpenUrl'; export const TauriLink: React.FC<LinkProps & any> = (props) => { const { href, onClick, ...restProps } = props; @@ -12,7 +12,7 @@ export const TauriLink: React.FC<LinkProps & any> = (props) => { if (href && (href.startsWith('http://') || href.startsWith('https://'))) { event.preventDefault(); - await openUrl(href); + await safeOpenUrl(href); } }; diff --git a/nym-wallet/src/components/TokenPoolSelector.stories.tsx b/nym-wallet/src/components/TokenPoolSelector.stories.tsx deleted file mode 100644 index 1247f5b00d..0000000000 --- a/nym-wallet/src/components/TokenPoolSelector.stories.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Box } from '@mui/material'; -import { TokenPoolSelector } from './TokenPoolSelector'; -import { MockMainContextProvider } from '../context/mocks/main'; - -export default { - title: 'Wallet / Token pool', - component: TokenPoolSelector, -} as ComponentMeta<typeof TokenPoolSelector>; - -const Template: ComponentStory<typeof TokenPoolSelector> = (args) => ( - <Box mt={2} height={800}> - <MockMainContextProvider> - <TokenPoolSelector {...args} /> - </MockMainContextProvider> - </Box> -); - -export const Default = Template.bind({}); -Default.args = { - disabled: false, - onSelect: () => {}, -}; diff --git a/nym-wallet/src/components/headerControlPillSx.ts b/nym-wallet/src/components/headerControlPillSx.ts new file mode 100644 index 0000000000..848b29872d --- /dev/null +++ b/nym-wallet/src/components/headerControlPillSx.ts @@ -0,0 +1,26 @@ +import type { SxProps, Theme } from '@mui/material/styles'; +import { alpha } from '@mui/material/styles'; + +/** + * Shared visual style for account and network controls in the AppBar pill. + */ +export const headerControlPillSx: SxProps<Theme> = { + color: 'text.primary', + fontSize: 14, + textTransform: 'none', + borderRadius: 999, + px: 2.25, + py: 1.25, + minHeight: 44, + lineHeight: 1.2, + border: (theme: Theme) => + theme.palette.mode === 'light' + ? `1px solid ${alpha(theme.palette.common.black, 0.08)}` + : `1px solid ${theme.palette.divider}`, + bgcolor: (theme: Theme) => + theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.06)' : alpha(theme.palette.background.paper, 0.85), + '&:hover': { + bgcolor: (theme: Theme) => + theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.1)' : alpha(theme.palette.common.black, 0.04), + }, +}; diff --git a/nym-wallet/src/components/index.ts b/nym-wallet/src/components/index.ts index 904d1a3097..4e73d053f2 100644 --- a/nym-wallet/src/components/index.ts +++ b/nym-wallet/src/components/index.ts @@ -4,6 +4,7 @@ export * from './Clipboard'; export * from './ErrorFallback'; export * from './InfoToolTip'; export * from './LoadingPage'; +export * from './AppSessionLoadingOverlay'; export * from './Mnemonic'; export * from './Nav'; export * from './NetworkSelector'; diff --git a/nym-wallet/src/context/main.tsx b/nym-wallet/src/context/main.tsx index 7ae2750478..b8a43546ab 100644 --- a/nym-wallet/src/context/main.tsx +++ b/nym-wallet/src/context/main.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useEffect, useMemo, useState } from 'react'; +import React, { createContext, useCallback, useEffect, useMemo, useState } from 'react'; import { forage } from '@tauri-apps/tauri-forage'; import { useNavigate } from 'react-router-dom'; import { useSnackbar } from 'notistack'; @@ -35,6 +35,9 @@ export const urls = (networkName?: Network) => type TLoginType = 'mnemonic' | 'password'; +/** `auth-splash`: full-screen auth-style loader (fallback). `app-overlay`: blurred in-app card (sign-in, account switch, sign-out). */ +export type AppLoadingPresentation = 'auth-splash' | 'app-overlay'; + export type TAppContext = { mode: 'light' | 'dark'; appEnv?: AppEnv; @@ -47,6 +50,11 @@ export type TAppContext = { showTerminal: boolean; network?: Network; isLoading: boolean; + /** While `isLoading` is true, selects {@link LoadingPage} vs {@link AppSessionLoadingOverlay}. */ + loadingPresentation: AppLoadingPresentation; + /** Short line shown on the in-app loading overlay. */ + loadingOverlayTitle: string; + loadingOverlaySubtitle?: string; isAdminAddress: boolean; error?: string; loginType?: TLoginType; @@ -56,6 +64,8 @@ export type TAppContext = { handleSwitchMode: () => void; handleShowSendModal: () => void; handleShowReceiveModal: () => void; + handleCloseSendModal: () => void; + handleCloseReceiveModal: () => void; setIsLoading: (isLoading: boolean) => void; setError: (value?: string) => void; switchNetwork: (network: Network) => void; @@ -86,10 +96,24 @@ export const AppProvider: FCWithChildren = ({ children }) => { const [appEnv, setAppEnv] = useState<AppEnv>(); const [showAdmin, setShowAdmin] = useState(false); const [showTerminal, setShowTerminal] = useState(false); - const [mode, setMode] = useState<'light' | 'dark'>('light'); + /** Default dark avoids a light flash before forage restores a saved preference. */ + const [mode, setMode] = useState<'light' | 'dark'>('dark'); const [loginType, setLoginType] = useState<'mnemonic' | 'password'>(); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoadingInternal] = useState(false); + const [loadingPresentation, setLoadingPresentation] = useState<AppLoadingPresentation>('auth-splash'); + const [loadingOverlayTitle, setLoadingOverlayTitle] = useState(''); + const [loadingOverlaySubtitle, setLoadingOverlaySubtitle] = useState<string | undefined>(); const [error, setError] = useState<string>(); + + /** Context-exposed setter: turning loading off also resets overlay copy and presentation to defaults. */ + const publishSetIsLoading = useCallback((loading: boolean) => { + setIsLoadingInternal(loading); + if (!loading) { + setLoadingPresentation('auth-splash'); + setLoadingOverlayTitle(''); + setLoadingOverlaySubtitle(undefined); + } + }, []); const [appVersion, setAppVersion] = useState<string>(); const [isAdminAddress, setIsAdminAddress] = useState<boolean>(false); const [showSendModal, setShowSendModal] = useState(false); @@ -129,7 +153,7 @@ export const AppProvider: FCWithChildren = ({ children }) => { setStoredAccounts(undefined); setNetwork(undefined); setError(undefined); - setIsLoading(false); + publishSetIsLoading(false); setMixnodeDetails(null); }; @@ -245,7 +269,14 @@ export const AppProvider: FCWithChildren = ({ children }) => { return; } try { - setIsLoading(true); + setLoadingPresentation('app-overlay'); + setLoadingOverlayTitle('Signing in'); + setLoadingOverlaySubtitle( + type === 'mnemonic' + ? 'Restoring your wallet from your recovery phrase.' + : 'Unlocking your wallet and connecting to the network.', + ); + setIsLoadingInternal(true); if (type === 'mnemonic') { await signInWithMnemonic(value); setLoginType('mnemonic'); @@ -258,12 +289,15 @@ export const AppProvider: FCWithChildren = ({ children }) => { } catch (e) { setError(e as string); } finally { - setIsLoading(false); + publishSetIsLoading(false); } }; const logOut = async () => { - setIsLoading(true); + setLoadingPresentation('app-overlay'); + setLoadingOverlayTitle('Signing out'); + setLoadingOverlaySubtitle('Closing your session safely.'); + setIsLoadingInternal(true); try { await signOut(); await setReactState(undefined); @@ -271,13 +305,16 @@ export const AppProvider: FCWithChildren = ({ children }) => { enqueueSnackbar('Successfully logged out', { variant: 'success' }); await createSignInWindow(); } finally { - setIsLoading(false); + publishSetIsLoading(false); } }; const onAccountChange = async ({ accountId, password }: { accountId: string; password: string }) => { if (network) { - setIsLoading(true); + setLoadingPresentation('app-overlay'); + setLoadingOverlayTitle('Switching account'); + setLoadingOverlaySubtitle('Refreshing your wallet and balances.'); + setIsLoadingInternal(true); try { await switchAccount({ accountId, password }); await loadAccount(network); @@ -285,7 +322,7 @@ export const AppProvider: FCWithChildren = ({ children }) => { } catch (e) { throw new Error(`Error swtiching account: ${e}`); } finally { - setIsLoading(false); + publishSetIsLoading(false); } } }; @@ -293,8 +330,10 @@ export const AppProvider: FCWithChildren = ({ children }) => { const handleShowAdmin = () => setShowAdmin((show) => !show); const handleShowTerminal = () => setShowTerminal((show) => !show); const switchNetwork = (_network: Network) => setNetwork(_network); - const handleShowSendModal = () => setShowSendModal((show) => !show); - const handleShowReceiveModal = () => setShowReceiveModal((show) => !show); + const handleShowSendModal = () => setShowSendModal(true); + const handleShowReceiveModal = () => setShowReceiveModal(true); + const handleCloseSendModal = () => setShowSendModal(false); + const handleCloseReceiveModal = () => setShowReceiveModal(false); const handleSwitchMode = () => setMode((currentMode) => { const newMode = currentMode === 'light' ? 'dark' : 'light'; @@ -303,12 +342,15 @@ export const AppProvider: FCWithChildren = ({ children }) => { }); const memoizedValue = useMemo( - () => ({ + (): TAppContext => ({ mode, appEnv, appVersion, isAdminAddress, isLoading, + loadingPresentation, + loadingOverlayTitle, + loadingOverlaySubtitle, error, clientDetails, storedAccounts, @@ -318,7 +360,7 @@ export const AppProvider: FCWithChildren = ({ children }) => { showTerminal, network, loginType, - setIsLoading, + setIsLoading: publishSetIsLoading, setError, signInWithPassword, switchNetwork, @@ -333,6 +375,8 @@ export const AppProvider: FCWithChildren = ({ children }) => { showReceiveModal, handleShowSendModal, handleShowReceiveModal, + handleCloseSendModal, + handleCloseReceiveModal, handleSwitchMode, printBalance, printVestedBalance, @@ -345,6 +389,9 @@ export const AppProvider: FCWithChildren = ({ children }) => { mode, appEnv, isLoading, + loadingPresentation, + loadingOverlayTitle, + loadingOverlaySubtitle, error, clientDetails, mixnodeDetails, @@ -356,6 +403,7 @@ export const AppProvider: FCWithChildren = ({ children }) => { showSendModal, showReceiveModal, mixnetContractParams, + publishSetIsLoading, ], ); diff --git a/nym-wallet/src/context/mocks/main.tsx b/nym-wallet/src/context/mocks/main.tsx index cb68ead589..5e30270183 100644 --- a/nym-wallet/src/context/mocks/main.tsx +++ b/nym-wallet/src/context/mocks/main.tsx @@ -5,7 +5,7 @@ import { AppContext } from '../main'; export const MockMainContextProvider: FCWithChildren = ({ children }) => { const memoizedValue = useMemo<TAppContext>( () => ({ - mode: 'light', + mode: 'dark', handleSwitchMode: () => undefined, appEnv: { ADMIN_ADDRESS: null, @@ -15,11 +15,16 @@ export const MockMainContextProvider: FCWithChildren = ({ children }) => { appVersion: 'mock', isAdminAddress: false, isLoading: false, + loadingPresentation: 'auth-splash', + loadingOverlayTitle: '', + loadingOverlaySubtitle: undefined, clientDetails: { display_mix_denom: 'nymt', base_mix_denom: 'unymt', client_address: '', }, + storedAccounts: undefined, + mixnodeDetails: null, userBalance: { balance: { amount: { @@ -28,18 +33,21 @@ export const MockMainContextProvider: FCWithChildren = ({ children }) => { }, printable_balance: '100 NYMT', }, + error: undefined, + tokenAllocation: undefined, + originalVesting: undefined, + currentVestingPeriod: undefined, + vestingAccountInfo: undefined, clearAll: () => undefined, isLoading: false, clearBalance: () => undefined, fetchBalance: async () => undefined, fetchTokenAllocation: async () => undefined, - refreshBalances: async () => {}, + refreshBalances: async () => undefined, }, - displayDenom: 'NYM', showAdmin: false, showTerminal: false, - showSettings: false, - showSendModal: true, + showSendModal: false, showReceiveModal: false, network: 'SANDBOX', loginType: 'mnemonic', @@ -53,11 +61,14 @@ export const MockMainContextProvider: FCWithChildren = ({ children }) => { logIn: () => undefined, logOut: () => undefined, onAccountChange: () => undefined, - handleShowSettings: () => undefined, handleShowSendModal: () => undefined, handleShowReceiveModal: () => undefined, + handleCloseSendModal: () => undefined, + handleCloseReceiveModal: () => undefined, keepState: async () => undefined, printBalance: '100.0000 NYMT', + printVestedBalance: undefined, + mixnetContractParams: undefined, }), [], ); diff --git a/nym-wallet/src/hooks/useGetBalance.ts b/nym-wallet/src/hooks/useGetBalance.ts index d43eeb1c04..e84cdd5cfb 100644 --- a/nym-wallet/src/hooks/useGetBalance.ts +++ b/nym-wallet/src/hooks/useGetBalance.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { Account, Balance, DecCoin, OriginalVestingResponse, Period, VestingAccountInfo } from '@nymproject/types'; import { getVestingCoins, @@ -33,12 +33,21 @@ export type TUseuserBalance = { vestingAccountInfo?: VestingAccountInfo; isLoading: boolean; fetchBalance: () => Promise<void>; - fetchTokenAllocation: () => Promise<void>; + fetchTokenAllocation: (isBackgroundPoll?: boolean) => Promise<void>; clearBalance: () => void; clearAll: () => void; refreshBalances: () => Promise<void>; }; +/** `BackendError::VestingContractAccountNotFound`, or legacy vesting ABCI wording. */ +const isNoVestingAccountError = (e: unknown) => { + const s = String(e); + return ( + s === 'Vesting contract has no account for this address' || + (s.includes('VESTING') && s.includes('Account does not exist')) + ); +}; + export const useGetBalance = (clientDetails?: Account): TUseuserBalance => { const [balance, setBalance] = useState<Balance>(); const [error, setError] = useState<string>(); @@ -48,16 +57,82 @@ export const useGetBalance = (clientDetails?: Account): TUseuserBalance => { const [vestingAccountInfo, setVestingAccountInfo] = useState<VestingAccountInfo>(); const [isLoading, setIsLoading] = useState(false); + const vestingAccountStatusRef = useRef<'unknown' | 'absent' | 'present'>('unknown'); + const clearBalance = () => setBalance(undefined); const clearTokenAllocation = () => setTokenAllocation(undefined); const clearOriginalVesting = () => setOriginalVesting(undefined); - const fetchTokenAllocation = async () => { + const clearVestingUiState = () => { + clearTokenAllocation(); + clearOriginalVesting(); + setCurrentVestingPeriod(undefined); + setVestingAccountInfo(undefined); + }; + + useEffect(() => { + vestingAccountStatusRef.current = 'unknown'; + }, [clientDetails?.client_address]); + + const applyAllocationFromResponses = ( + originalVestingValue: OriginalVestingResponse, + vestingCoins: Awaited<ReturnType<typeof getVestingCoins>>, + vestedCoins: Awaited<ReturnType<typeof getVestedCoins>>, + lockedCoins: Awaited<ReturnType<typeof getLockedCoins>>, + spendableCoins: Awaited<ReturnType<typeof getSpendableCoins>>, + spendableVestedCoins: Awaited<ReturnType<typeof getSpendableVestedCoins>>, + spendableRewardCoins: Awaited<ReturnType<typeof getSpendableRewardCoins>>, + currentPeriod: Awaited<ReturnType<typeof getCurrentVestingPeriod>>, + vestingAccountDetail: Awaited<ReturnType<typeof getVestingAccountInfo>>, + ) => { + setOriginalVesting(originalVestingValue); + setCurrentVestingPeriod(currentPeriod); + setTokenAllocation({ + vesting: vestingCoins.amount, + vested: vestedCoins.amount, + locked: lockedCoins.amount, + spendable: spendableCoins.amount, + spendableVestedCoins: spendableVestedCoins.amount, + spendableRewardCoins: spendableRewardCoins.amount, + }); + setVestingAccountInfo(vestingAccountDetail); + }; + + const fetchTokenAllocation = async (isBackgroundPoll = false) => { setIsLoading(true); - if (clientDetails?.client_address) { - try { + if (!clientDetails?.client_address) { + setIsLoading(false); + return; + } + + if (vestingAccountStatusRef.current === 'absent') { + if (isBackgroundPoll) { + setIsLoading(false); + return; + } + vestingAccountStatusRef.current = 'unknown'; + } + + const address = clientDetails.client_address; + + try { + if (vestingAccountStatusRef.current === 'unknown') { + let originalVestingValue: OriginalVestingResponse; + try { + originalVestingValue = await getOriginalVesting(address); + } catch (e) { + if (isNoVestingAccountError(e)) { + vestingAccountStatusRef.current = 'absent'; + clearVestingUiState(); + return; + } + clearVestingUiState(); + Console.error(e as string); + return; + } + + vestingAccountStatusRef.current = 'present'; const [ - originalVestingValue, vestingCoins, vestedCoins, lockedCoins, @@ -67,34 +142,70 @@ export const useGetBalance = (clientDetails?: Account): TUseuserBalance => { currentPeriod, vestingAccountDetail, ] = await Promise.all([ - getOriginalVesting(clientDetails?.client_address), - getVestingCoins(clientDetails?.client_address), - getVestedCoins(clientDetails?.client_address), + getVestingCoins(address), + getVestedCoins(address), getLockedCoins(), getSpendableCoins(), getSpendableVestedCoins(), getSpendableRewardCoins(), - getCurrentVestingPeriod(clientDetails?.client_address), - getVestingAccountInfo(clientDetails?.client_address), + getCurrentVestingPeriod(address), + getVestingAccountInfo(address), ]); - setOriginalVesting(originalVestingValue); - setCurrentVestingPeriod(currentPeriod); - setTokenAllocation({ - vesting: vestingCoins.amount, - vested: vestedCoins.amount, - locked: lockedCoins.amount, - spendable: spendableCoins.amount, - spendableVestedCoins: spendableVestedCoins.amount, - spendableRewardCoins: spendableRewardCoins.amount, - }); - setVestingAccountInfo(vestingAccountDetail); - } catch (e) { - clearTokenAllocation(); - clearOriginalVesting(); - Console.error(e as string); + applyAllocationFromResponses( + originalVestingValue, + vestingCoins, + vestedCoins, + lockedCoins, + spendableCoins, + spendableVestedCoins, + spendableRewardCoins, + currentPeriod, + vestingAccountDetail, + ); + return; } + + const [ + originalVestingValue, + vestingCoins, + vestedCoins, + lockedCoins, + spendableCoins, + spendableVestedCoins, + spendableRewardCoins, + currentPeriod, + vestingAccountDetail, + ] = await Promise.all([ + getOriginalVesting(address), + getVestingCoins(address), + getVestedCoins(address), + getLockedCoins(), + getSpendableCoins(), + getSpendableVestedCoins(), + getSpendableRewardCoins(), + getCurrentVestingPeriod(address), + getVestingAccountInfo(address), + ]); + applyAllocationFromResponses( + originalVestingValue, + vestingCoins, + vestedCoins, + lockedCoins, + spendableCoins, + spendableVestedCoins, + spendableRewardCoins, + currentPeriod, + vestingAccountDetail, + ); + } catch (e) { + if (isNoVestingAccountError(e)) { + vestingAccountStatusRef.current = 'absent'; + } + clearVestingUiState(); + Console.error(e as string); + } finally { + setIsLoading(false); } - setIsLoading(false); }; const fetchBalance = useCallback(async () => { @@ -114,9 +225,13 @@ export const useGetBalance = (clientDetails?: Account): TUseuserBalance => { clearBalance(); clearTokenAllocation(); clearOriginalVesting(); + setCurrentVestingPeriod(undefined); + setVestingAccountInfo(undefined); + vestingAccountStatusRef.current = 'unknown'; }; const refreshBalances = async () => { + vestingAccountStatusRef.current = 'unknown'; if (clientDetails?.client_address) { await fetchBalance(); await fetchTokenAllocation(); diff --git a/nym-wallet/src/hooks/useNymUsdPrice.ts b/nym-wallet/src/hooks/useNymUsdPrice.ts new file mode 100644 index 0000000000..ee6cdd2e5a --- /dev/null +++ b/nym-wallet/src/hooks/useNymUsdPrice.ts @@ -0,0 +1,59 @@ +import { useEffect, useMemo, useState } from 'react'; +import { fetchNymPriceDeduped, getNetworkOverviewEndpoints } from 'src/api/networkOverview'; +import type { Network } from 'src/types'; + +export type UseNymUsdPrice = { + usdPerNym: number | undefined; + loading: boolean; + error: string | undefined; +}; + +export function useNymUsdPrice(network: Network | undefined): UseNymUsdPrice { + const [usdPerNym, setUsdPerNym] = useState<number | undefined>(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState<string | undefined>(); + + const url = useMemo(() => { + if (network === undefined) { + return undefined; + } + return getNetworkOverviewEndpoints(network).nymPrice; + }, [network]); + + useEffect(() => { + if (!url) { + setUsdPerNym(undefined); + setLoading(false); + setError(undefined); + return undefined; + } + + let cancelled = false; + setLoading(true); + setError(undefined); + + fetchNymPriceDeduped(url) + .then((data) => { + if (!cancelled) { + setUsdPerNym(data.quotes.USD.price); + } + }) + .catch(() => { + if (!cancelled) { + setUsdPerNym(undefined); + setError('Could not load NYM price'); + } + }) + .finally(() => { + if (!cancelled) { + setLoading(false); + } + }); + + return (): void => { + cancelled = true; + }; + }, [url]); + + return { usdPerNym, loading, error }; +} diff --git a/nym-wallet/src/hooks/useTauriTextEditingClipboard.ts b/nym-wallet/src/hooks/useTauriTextEditingClipboard.ts new file mode 100644 index 0000000000..17b77676c4 --- /dev/null +++ b/nym-wallet/src/hooks/useTauriTextEditingClipboard.ts @@ -0,0 +1,91 @@ +import { useEffect } from 'react'; +import { readText, writeText } from '@tauri-apps/plugin-clipboard-manager'; + +function isTextEditingElement(el: EventTarget | null): el is HTMLInputElement | HTMLTextAreaElement { + if (!el || !(el instanceof HTMLElement)) return false; + if (el.closest('[data-nym-currency-field]')) return false; + /** Mnemonic login uses replace-whole-value paste + trim (must not insert at caret). */ + if (el.closest('[data-nym-auth-paste-field]')) return false; + /** Send/delegate/bond fields pass clipboard to React as a full replacement, not insertion. */ + if (el.closest('[data-nym-paste-replace]')) return false; + if (el instanceof HTMLTextAreaElement) return !el.disabled && !el.readOnly; + if (el instanceof HTMLInputElement) { + if (el.disabled || el.readOnly) return false; + const blocked = ['button', 'checkbox', 'radio', 'submit', 'file', 'reset', 'image']; + return !blocked.includes(el.type); + } + return false; +} + +/** Input types where copying the whole value on Cmd+C (no selection) matches typical desktop behavior. */ +function isSingleLineTextLikeInput(el: HTMLInputElement): boolean { + const t = el.type; + return t === 'text' || t === 'search' || t === 'tel' || t === 'url' || t === 'email' || t === 'password' || t === ''; +} + +function setNativeValue(el: HTMLInputElement | HTMLTextAreaElement, value: string) { + const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype; + const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set; + setter?.call(el, value); + el.dispatchEvent(new Event('input', { bubbles: true })); + el.dispatchEvent(new Event('change', { bubbles: true })); +} + +/** + * WebKit/WebView in Tauri often breaks native copy/paste in text fields. Route clipboard through the + * Tauri clipboard plugin when the user triggers standard shortcuts while focused in an input. + */ +export function useTauriTextEditingClipboard() { + useEffect(() => { + const onKeyDown = async (e: KeyboardEvent) => { + if (!e.ctrlKey && !e.metaKey) return; + if (e.defaultPrevented) return; + if (!isTextEditingElement(e.target)) return; + + const el = e.target; + + if (e.key === 'v') { + e.preventDefault(); + try { + const text = await readText(); + if (text == null) return; + const start = el.selectionStart ?? 0; + const end = el.selectionEnd ?? 0; + const next = `${el.value.slice(0, start)}${text}${el.value.slice(end)}`; + setNativeValue(el, next); + const caret = start + text.length; + el.setSelectionRange(caret, caret); + } catch { + /* fall through - user can retry from context menu */ + } + return; + } + + if (e.key === 'c' || e.key === 'x') { + const start = el.selectionStart ?? 0; + const end = el.selectionEnd ?? 0; + let selected = el.value.slice(start, end); + if (!selected && e.key === 'c' && el instanceof HTMLInputElement && isSingleLineTextLikeInput(el)) { + selected = el.value; + } + if (!selected) return; + e.preventDefault(); + try { + await writeText(selected); + } catch { + return; + } + if (e.key === 'x') { + if (start !== end) { + const next = `${el.value.slice(0, start)}${el.value.slice(end)}`; + setNativeValue(el, next); + el.setSelectionRange(start, start); + } + } + } + }; + + window.addEventListener('keydown', onKeyDown, true); + return () => window.removeEventListener('keydown', onKeyDown, true); + }, []); +} diff --git a/nym-wallet/src/layouts/AppLayout.tsx b/nym-wallet/src/layouts/AppLayout.tsx index 64b186b445..86c24257c4 100644 --- a/nym-wallet/src/layouts/AppLayout.tsx +++ b/nym-wallet/src/layouts/AppLayout.tsx @@ -1,53 +1,110 @@ import React, { useContext } from 'react'; import { NymWordmark } from '@nymproject/react/logo/NymWordmark'; -import { Box, Container, Typography } from '@mui/material'; -import { useTheme } from '@mui/material/styles'; +import { Box, Divider, Stack, Typography } from '@mui/material'; +import { alpha } from '@mui/material/styles'; import { AppContext } from 'src/context'; -import { AppBar, LoadingPage, Nav } from '../components'; +import { AppBar, AppSessionLoadingOverlay, LoadingPage, Nav } from '../components'; export const ApplicationLayout: FCWithChildren = ({ children }) => { - const theme = useTheme(); - const { isLoading, appVersion } = useContext(AppContext); + const { isLoading, loadingPresentation, loadingOverlayTitle, loadingOverlaySubtitle, appVersion } = + useContext(AppContext); return ( <> - {isLoading && <LoadingPage />} + {isLoading && + (loadingPresentation === 'app-overlay' ? ( + <AppSessionLoadingOverlay title={loadingOverlayTitle} subtitle={loadingOverlaySubtitle} /> + ) : ( + <LoadingPage /> + ))} <Box sx={{ height: '100vh', - width: '100vw', - display: 'grid', - gridTemplateColumns: '240px auto', - gridTemplateRows: '100%', + maxHeight: '100vh', + width: '100%', + maxWidth: '100vw', overflow: 'hidden', + display: 'grid', + gridTemplateColumns: { xs: '1fr', lg: '300px minmax(0, 1fr)' }, + background: (t) => + t.palette.mode === 'dark' + ? `linear-gradient(180deg, ${t.palette.nym.nymWallet.topNav.background} 0%, ${t.palette.background.default} 100%)` + : t.palette.background.default, }} > <Box sx={{ - background: (t) => t.palette.nym.nymWallet.nav.background, - overflow: 'auto', - py: 4, + display: { xs: 'none', lg: 'flex' }, + flexDirection: 'column', + justifyContent: 'space-between', + height: '100%', + minHeight: 0, + overflowY: 'auto', + overflowX: 'hidden', + scrollbarGutter: 'stable', + px: 3, + py: 3, }} - display="flex" - flexDirection="column" - justifyContent="space-between" > - <Box> - <Box sx={{ ml: 5, mb: 7 }}> - <NymWordmark height={14} /> - </Box> + <Stack + spacing={3} + sx={{ + p: 3, + borderRadius: 4, + bgcolor: 'nym.nymWallet.nav.background', + border: (t) => + t.palette.mode === 'light' + ? `1px solid ${alpha(t.palette.common.black, 0.08)}` + : `1px solid ${t.palette.divider}`, + boxShadow: (t) => t.palette.nym.nymWallet.shadows.medium, + }} + > + <Stack spacing={1.5}> + <NymWordmark height={16} /> + </Stack> + <Divider /> <Nav /> - </Box> - {appVersion && ( - <Typography sx={{ color: 'grey.500', fontSize: 14, ml: 5, mt: 8 }}>Version {appVersion}</Typography> - )} + </Stack> + <Stack spacing={1} sx={{ px: 1, pt: 3 }}> + <Typography + variant="caption" + sx={{ color: 'nym.text.muted', textTransform: 'uppercase', letterSpacing: 1 }} + > + Nym Wallet + </Typography> + {appVersion ? ( + <Typography sx={{ color: 'text.secondary', fontSize: 14 }}>Version {appVersion}</Typography> + ) : null} + </Stack> </Box> - <Container maxWidth="xl"> + <Box + sx={{ + minWidth: 0, + maxWidth: '100%', + height: '100%', + minHeight: 0, + overflow: 'hidden', + display: 'flex', + flexDirection: 'column', + px: { xs: 2, md: 3, xl: 4 }, + pb: { xs: 3, md: 4 }, + }} + > <AppBar /> - <Box overflow="auto" sx={{ height: () => `calc(100% - ${theme.spacing(10)})` }}> + <Box + sx={{ + flex: '1 1 auto', + minHeight: 0, + overflowY: 'auto', + overflowX: 'hidden', + pr: { xs: 0, md: 1 }, + // Avoid horizontal layout shift when scrollbar appears between short/tall routes (e.g. delegation vs bonding). + scrollbarGutter: 'stable', + }} + > {children} </Box> - </Container> + </Box> </Box> </> ); diff --git a/nym-wallet/src/layouts/AuthLayout.tsx b/nym-wallet/src/layouts/AuthLayout.tsx index 6e951b136e..a198db8e06 100644 --- a/nym-wallet/src/layouts/AuthLayout.tsx +++ b/nym-wallet/src/layouts/AuthLayout.tsx @@ -1,39 +1,106 @@ import React, { useContext } from 'react'; import { NymWordmark } from '@nymproject/react/logo/NymWordmark'; -import { Stack, Box } from '@mui/material'; +import { Stack, Box, Typography } from '@mui/material'; +import { alpha, useTheme } from '@mui/material/styles'; import { AppContext } from 'src/context'; -import { LoadingPage } from 'src/components'; +import { AppSessionLoadingOverlay, LoadingPage } from 'src/components'; import { Step } from '../pages/auth/components/step'; export const AuthLayout: FCWithChildren = ({ children }) => { - const { isLoading } = useContext(AppContext); + const { isLoading, loadingPresentation, loadingOverlayTitle, loadingOverlaySubtitle } = useContext(AppContext); + const theme = useTheme(); + const isDark = theme.palette.mode === 'dark'; + const wordmarkFill = isDark ? '#FFFFFF' : theme.palette.text.primary; - return isLoading ? ( - <LoadingPage /> - ) : ( + if (isLoading) { + if (loadingPresentation === 'app-overlay') { + return <AppSessionLoadingOverlay title={loadingOverlayTitle} subtitle={loadingOverlaySubtitle} />; + } + return <LoadingPage />; + } + + return ( <Box sx={{ - height: '100vh', - width: '100vw', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - overflow: 'auto', - bgcolor: 'nym.background.dark', + minHeight: '100vh', + width: '100%', + display: 'grid', + // Below 2000px: single column (sign-in card centered). At 2000px+: optional left strip of static marketing copy (not navigation). + gridTemplateColumns: '1fr', + '@media (min-width: 2000px)': { + gridTemplateColumns: 'minmax(320px, 420px) minmax(0, 1fr)', + }, + alignItems: 'stretch', + bgcolor: 'background.default', }} > <Box sx={{ width: '100%', - display: 'flex', - justifyContent: 'center', - margin: 'auto', + display: { xs: 'none' }, + '@media (min-width: 2000px)': { + display: 'flex', + }, + flexDirection: 'column', + justifyContent: 'space-between', + px: 6, + py: 7, + borderRight: (t) => `1px solid ${t.palette.divider}`, + background: isDark ? alpha(theme.palette.common.black, 0.2) : theme.palette.nym.nymWallet.background.subtle, }} > - <Stack spacing={3} alignItems="center" sx={{ width: 1080 }}> - <NymWordmark width={75} fill="#FFFFFF" /> - <Step /> - {children} + <Stack spacing={3}> + <NymWordmark width={88} fill={wordmarkFill} /> + <Stack spacing={1}> + <Typography variant="overline" sx={{ color: 'text.secondary', letterSpacing: 1.4 }}> + Secure wallet access + </Typography> + <Typography variant="h3" sx={{ maxWidth: 320, lineHeight: 1.1, color: 'text.primary' }}> + Access tokens, staking, and node operations in one place. + </Typography> + <Typography variant="body1" sx={{ color: 'text.secondary', maxWidth: 340 }}> + The wallet keeps the same trusted Nym visual language while making key tasks easier to scan and complete. + </Typography> + </Stack> + </Stack> + <Step /> + </Box> + <Box + sx={{ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + px: { xs: 2, md: 4 }, + py: { xs: 4, md: 6 }, + }} + > + <Stack spacing={3} alignItems="center" sx={{ width: '100%', maxWidth: 1080 }}> + <Box + sx={{ + display: 'block', + '@media (min-width: 2000px)': { display: 'none' }, + }} + > + <NymWordmark width={75} fill={wordmarkFill} /> + </Box> + <Box + sx={{ + width: '100%', + maxWidth: 480, + borderRadius: 4, + bgcolor: isDark ? alpha(theme.palette.background.paper, 0.92) : 'background.paper', + border: (t) => `1px solid ${t.palette.divider}`, + boxShadow: (t) => t.palette.nym.nymWallet.shadows.strong, + px: { xs: 3, sm: 4 }, + py: { xs: 3, sm: 4 }, + boxSizing: 'border-box', + }} + > + <Stack spacing={2} sx={{ width: '100%', alignItems: 'stretch' }}> + <Step /> + <Box sx={{ width: '100%' }}>{children}</Box> + </Stack> + </Box> </Stack> </Box> </Box> diff --git a/nym-wallet/src/layouts/PageLayout.tsx b/nym-wallet/src/layouts/PageLayout.tsx index 3ab5d5f995..0e2d37f680 100644 --- a/nym-wallet/src/layouts/PageLayout.tsx +++ b/nym-wallet/src/layouts/PageLayout.tsx @@ -1,22 +1,23 @@ import React from 'react'; import { Box } from '@mui/material'; +import { ContentRailWidth, resolveContentRailMaxWidth } from './contentRail'; -export const PageLayout: FCWithChildren<{ position?: 'flex-start' | 'flex-end'; children: React.ReactNode }> = ({ - position, - children, -}) => ( +export const PageLayout: FCWithChildren<{ + position?: 'flex-start' | 'flex-end'; + maxWidth?: ContentRailWidth; + children: React.ReactNode; +}> = ({ position, maxWidth, children }) => ( <Box sx={{ - maxHeight: 'calc(100% - 65px)', + minHeight: '100%', display: 'flex', - flexFlow: 'column wrap', - justifyContent: 'start', - alignItems: position || 'center', - overflow: 'auto', - mt: 4, + flexDirection: 'column', + justifyContent: 'flex-start', + alignItems: position || 'stretch', + mt: { xs: 2, md: 3 }, }} > - <Box width="100%" margin="auto"> + <Box width="100%" maxWidth={resolveContentRailMaxWidth(maxWidth)} mx="auto"> {children} </Box> </Box> diff --git a/nym-wallet/src/layouts/contentRail.ts b/nym-wallet/src/layouts/contentRail.ts new file mode 100644 index 0000000000..47d565ca79 --- /dev/null +++ b/nym-wallet/src/layouts/contentRail.ts @@ -0,0 +1,17 @@ +/** Max inner content width for wide pages (Balance, etc.). Must match historical PageLayout default. */ +export const CONTENT_RAIL_MAX_WIDTH_WIDE = 1280; + +/** Narrow variant (when PageLayout maxWidth="narrow"). */ +export const CONTENT_RAIL_MAX_WIDTH_NARROW = 1000; + +export type ContentRailWidth = number | 'narrow' | 'wide'; + +export function resolveContentRailMaxWidth(maxWidth?: ContentRailWidth): number { + if (maxWidth === 'narrow') { + return CONTENT_RAIL_MAX_WIDTH_NARROW; + } + if (maxWidth === 'wide' || maxWidth === undefined) { + return CONTENT_RAIL_MAX_WIDTH_WIDE; + } + return maxWidth; +} diff --git a/nym-wallet/src/pages/auth/components/heading.tsx b/nym-wallet/src/pages/auth/components/heading.tsx index 210379d222..5ebfec215b 100644 --- a/nym-wallet/src/pages/auth/components/heading.tsx +++ b/nym-wallet/src/pages/auth/components/heading.tsx @@ -1,23 +1,49 @@ import React from 'react'; import { Typography } from '@mui/material'; -export const Title = ({ title }: { title: string }) => ( - <Typography sx={{ color: 'common.white', fontWeight: 600, fontSize: 20 }}>{title}</Typography> +export const Title = ({ title, align = 'left' }: { title: string; align?: 'left' | 'center' }) => ( + <Typography + component="h1" + sx={{ + color: 'text.primary', + fontWeight: 600, + fontSize: { xs: '1.25rem', sm: '1.35rem' }, + lineHeight: 1.3, + mb: 0.5, + width: '100%', + textAlign: align, + }} + > + {title} + </Typography> ); -export const Subtitle = ({ subtitle }: { subtitle: string }) => ( - <Typography sx={{ color: 'common.white', textAlign: 'center', maxWidth: 450 }}>{subtitle}</Typography> +export const Subtitle = ({ subtitle, align = 'left' }: { subtitle: string; align?: 'left' | 'center' }) => ( + <Typography + sx={{ + color: 'text.secondary', + textAlign: align, + maxWidth: align === 'center' ? 520 : 'none', + width: '100%', + }} + > + {subtitle} + </Typography> ); export const SubtitleSlick = ({ subtitle }: { subtitle: string }) => ( <Typography variant="caption" sx={{ - color: (theme) => theme.palette.text.disabled, + color: 'text.secondary', textTransform: 'uppercase', - letterSpacing: 4, - fontWeight: 400, - fontSize: 14, + letterSpacing: 2, + fontWeight: 500, + fontSize: 12, + display: 'block', + width: '100%', + mb: 2, + textAlign: 'center', }} > {subtitle} diff --git a/nym-wallet/src/pages/auth/components/passwordStrength.stories.tsx b/nym-wallet/src/pages/auth/components/passwordStrength.stories.tsx deleted file mode 100644 index 3746b2015a..0000000000 --- a/nym-wallet/src/pages/auth/components/passwordStrength.stories.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import * as React from 'react'; -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Stack, TextField } from '@mui/material'; -import { PasswordStrength } from './password-strength'; - -export default { - title: 'Wallet / Password Strength', - component: PasswordStrength, -} as ComponentMeta<typeof PasswordStrength>; - -const Template: ComponentStory<typeof PasswordStrength> = ({ password, withWarnings, handleIsSafePassword }) => { - const [value, setValue] = React.useState(password); - return ( - <Stack alignContent="center"> - <TextField value={value} onChange={(e) => setValue(e.target.value)} sx={{ mb: 0.5 }} /> - {!!password.length && ( - <PasswordStrength handleIsSafePassword={handleIsSafePassword} withWarnings={withWarnings} password={password} /> - )} - </Stack> - ); -}; - -export const VeryStrong = Template.bind({}); -VeryStrong.args = { password: 'fedgklnrf34£', withWarnings: true, handleIsSafePassword: () => undefined }; - -export const Strong = Template.bind({}); -Strong.args = { password: '"56%abc123?@', withWarnings: true, handleIsSafePassword: () => undefined }; - -export const Average = Template.bind({}); -Average.args = { password: '"abc123?', withWarnings: true, handleIsSafePassword: () => undefined }; - -export const Weak = Template.bind({}); -Weak.args = { password: 'abc123?', withWarnings: true, handleIsSafePassword: () => undefined }; - -export const VeryWeak = Template.bind({}); -VeryWeak.args = { - password: 'abc123', - withWarnings: true, - handleIsSafePassword: () => undefined, -}; - -export const WithName = Template.bind({}); -WithName.args = { - password: 'fred', - withWarnings: true, - handleIsSafePassword: () => undefined, -}; - -export const WithSequence = Template.bind({}); -WithSequence.args = { - password: '121212', - withWarnings: true, - handleIsSafePassword: () => undefined, -}; - -export const Default = Template.bind({}); -Default.args = { - password: 'abc123', - withWarnings: true, - handleIsSafePassword: () => undefined, -}; diff --git a/nym-wallet/src/pages/auth/components/word-tiles.tsx b/nym-wallet/src/pages/auth/components/word-tiles.tsx index 6c71f0fb08..6d336f86c1 100644 --- a/nym-wallet/src/pages/auth/components/word-tiles.tsx +++ b/nym-wallet/src/pages/auth/components/word-tiles.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Box, Card, CardHeader, Grid, Typography, Stack, Fade } from '@mui/material'; +import { Box, Card, CardHeader, Typography, Stack } from '@mui/material'; import { THiddenMnemonicWord, THiddenMnemonicWords, TMnemonicWords } from '../types'; export const WordTile = ({ @@ -17,22 +17,52 @@ export const WordTile = ({ }) => ( <Card variant="outlined" - sx={{ + sx={(theme) => ({ background: button ? '#151A2C' : 'transparent', - border: button ? '1px solid #3A4053' : 'none', + border: button ? `1px solid ${theme.palette.divider}` : 'none', cursor: onClick ? 'pointer' : 'default', - opacity: disabled ? 0.2 : 1, - }} + opacity: disabled ? 0.35 : 1, + minWidth: button ? 88 : undefined, + width: '100%', + transition: 'border-color 0.15s ease, opacity 0.15s ease', + ...(button && + onClick && { + '&:hover': { + transform: 'none', + borderColor: theme.palette.primary.main, + }, + }), + ...(!button && { + boxShadow: 'none', + '&:hover': { transform: 'none', boxShadow: 'none' }, + }), + })} > <CardHeader title={mnemonicWord} - titleTypographyProps={{ sx: { fontWeight: 700 }, variant: 'body1', textAlign: index ? 'left' : 'center' }} + titleTypographyProps={{ + sx: { + fontWeight: 700, + whiteSpace: 'normal', + overflow: 'visible', + textOverflow: 'clip', + wordBreak: 'break-word', + lineHeight: 1.25, + }, + variant: 'body2', + textAlign: index ? 'left' : 'center', + }} + sx={{ + py: 1.25, + px: 1.5, + '& .MuiCardHeader-content': { overflow: 'visible' }, + }} avatar={ - index && ( - <Typography variant="caption" color="#3A4053"> + index ? ( + <Typography variant="caption" color="text.secondary" sx={{ fontWeight: 600 }}> {index} </Typography> - ) + ) : undefined } /> </Card> @@ -51,9 +81,24 @@ export const WordTiles = ({ }) => { if (mnemonicWords) { return ( - <Grid container spacing={3} justifyContent="center"> + <Stack + direction="row" + flexWrap="wrap" + useFlexGap + spacing={2} + justifyContent="center" + sx={{ width: '100%', rowGap: 2, columnGap: 2 }} + > {mnemonicWords.map(({ name, index, disabled }) => ( - <Grid item xs={2} key={index} onClick={() => onClick?.({ name, index })}> + <Box + key={index} + onClick={() => onClick?.({ name, index })} + sx={{ + flex: '1 1 auto', + minWidth: { xs: 'calc(50% - 8px)', sm: 120 }, + maxWidth: { xs: '100%', sm: 200 }, + }} + > <WordTile mnemonicWord={name} index={showIndex ? index : undefined} @@ -61,9 +106,9 @@ export const WordTiles = ({ disabled={disabled} button={buttons} /> - </Grid> + </Box> ))} - </Grid> + </Stack> ); } @@ -71,28 +116,52 @@ export const WordTiles = ({ }; const HiddenWord = ({ mnemonicWord }: { mnemonicWord: THiddenMnemonicWord }) => ( - <Stack spacing={2} alignItems="center"> - <Box borderBottom="1px solid #3A4053" sx={{ p: 2, width: '100%' }}> - <Fade in={!mnemonicWord.hidden}> - <Box> - <WordTile mnemonicWord={mnemonicWord.name} /> - </Box> - </Fade> + <Stack spacing={1} alignItems="center" sx={{ flex: '1 1 auto', minWidth: { xs: 100, sm: 112 }, maxWidth: 180 }}> + <Typography variant="body2" color="text.secondary" fontWeight={600}> + {mnemonicWord.index}. + </Typography> + <Box + sx={(theme) => ({ + width: '100%', + minHeight: 52, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + borderBottom: `2px solid ${theme.palette.divider}`, + px: 0.5, + pb: 0.75, + })} + > + <Box sx={{ width: '100%', textAlign: 'center' }}> + {mnemonicWord.hidden ? ( + <Typography variant="caption" color="text.disabled" sx={{ userSelect: 'none', letterSpacing: 2 }}> + --- + </Typography> + ) : ( + <Typography fontWeight={700} variant="body2" sx={{ wordBreak: 'break-word', lineHeight: 1.3 }}> + {mnemonicWord.name} + </Typography> + )} + </Box> </Box> - <Typography>{mnemonicWord.index}.</Typography> </Stack> ); export const HiddenWords = ({ mnemonicWords }: { mnemonicWords?: THiddenMnemonicWords }) => { if (mnemonicWords) { return ( - <Grid container spacing={3} justifyContent="center"> + <Stack + direction="row" + flexWrap="wrap" + useFlexGap + spacing={2} + justifyContent="center" + sx={{ width: '100%', mb: 1, rowGap: 2, columnGap: 2 }} + > {mnemonicWords.map((mnemonicWord) => ( - <Grid item xs={2} key={mnemonicWord.index}> - <HiddenWord mnemonicWord={mnemonicWord} /> - </Grid> + <HiddenWord key={mnemonicWord.index} mnemonicWord={mnemonicWord} /> ))} - </Grid> + </Stack> ); } return null; diff --git a/nym-wallet/src/pages/auth/pages/existing-account.tsx b/nym-wallet/src/pages/auth/pages/existing-account.tsx index 20b5d90c36..1734614df2 100644 --- a/nym-wallet/src/pages/auth/pages/existing-account.tsx +++ b/nym-wallet/src/pages/auth/pages/existing-account.tsx @@ -2,23 +2,34 @@ import React from 'react'; import { useNavigate } from 'react-router-dom'; import { Box, Button, Stack, Typography } from '@mui/material'; -import { SubtitleSlick, Title } from '../components'; +import { SubtitleSlick } from '../components'; export const ExistingAccount = () => { const navigate = useNavigate(); return ( - <> - <Title title="Welcome to Nym" /> - <SubtitleSlick subtitle="NEXT GENERATION OF PRIVACY" /> - <Stack spacing={2} sx={{ width: 300 }}> - <Button variant="contained" size="large" onClick={() => navigate('/sign-in-mnemonic')} fullWidth> + <Stack spacing={1.5} sx={{ width: '100%', alignItems: 'stretch' }}> + <SubtitleSlick subtitle="Next generation of privacy" /> + <Stack spacing={2} sx={{ width: '100%', pt: 0.5 }}> + <Button + variant="contained" + size="large" + onClick={() => navigate('/sign-in-mnemonic')} + fullWidth + sx={{ py: 1.25 }} + > Sign in with mnemonic </Button> - <Typography sx={{ textAlign: 'center', fontWeight: 600 }}>or</Typography> - <Button variant="contained" size="large" fullWidth onClick={() => navigate('/sign-in-password')}> + <Typography sx={{ textAlign: 'center', fontWeight: 600, color: 'text.secondary' }}>or</Typography> + <Button + variant="contained" + size="large" + fullWidth + sx={{ py: 1.25 }} + onClick={() => navigate('/sign-in-password')} + > Sign in with password </Button> - <Box display="flex" justifyContent="space-between"> + <Box display="flex" justifyContent="center" alignItems="center" flexWrap="wrap" gap={2} sx={{ pt: 1 }}> <Button color="inherit" onClick={() => navigate('/')}> Back </Button> @@ -27,6 +38,6 @@ export const ExistingAccount = () => { </Button> </Box> </Stack> - </> + </Stack> ); }; diff --git a/nym-wallet/src/pages/auth/pages/forgot-password.tsx b/nym-wallet/src/pages/auth/pages/forgot-password.tsx index 0bf535d391..fb8acc01e3 100644 --- a/nym-wallet/src/pages/auth/pages/forgot-password.tsx +++ b/nym-wallet/src/pages/auth/pages/forgot-password.tsx @@ -1,26 +1,41 @@ /* eslint-disable react/no-unused-prop-types */ import React from 'react'; import { useNavigate } from 'react-router-dom'; -import { Button, Stack, Typography } from '@mui/material'; -import { Subtitle } from '../components'; +import { Box, Button, Stack, Typography } from '@mui/material'; +import { Subtitle, Title } from '../components'; export const ForgotPassword = () => { const navigate = useNavigate(); return ( - <> - <Subtitle subtitle="Create a new password or sign in with mnemonic" /> - <Stack spacing={2} sx={{ width: 300 }}> - <Button variant="contained" size="large" onClick={() => navigate('/confirm-mnemonic')} fullWidth> + <Stack spacing={1.5} sx={{ width: '100%', alignItems: 'stretch' }}> + <Title title="Forgot password" align="center" /> + <Subtitle subtitle="Create a new password or sign in with mnemonic" align="center" /> + <Stack spacing={2} sx={{ width: '100%', pt: 0.5 }}> + <Button + variant="contained" + size="large" + onClick={() => navigate('/confirm-mnemonic')} + fullWidth + sx={{ py: 1.25 }} + > Create a new password </Button> - <Typography sx={{ textAlign: 'center', fontWeight: 600 }}>or</Typography> - <Button size="large" variant="contained" fullWidth onClick={() => navigate('/sign-in-mnemonic')}> + <Typography sx={{ textAlign: 'center', fontWeight: 600, color: 'text.secondary' }}>or</Typography> + <Button + size="large" + variant="contained" + fullWidth + sx={{ py: 1.25 }} + onClick={() => navigate('/sign-in-mnemonic')} + > Sign in with mnemonic </Button> - <Button color="inherit" onClick={() => navigate(-1)}> - Back - </Button> + <Box display="flex" justifyContent="center" alignItems="center" sx={{ pt: 1 }}> + <Button color="inherit" onClick={() => navigate(-1)}> + Back + </Button> + </Box> </Stack> - </> + </Stack> ); }; diff --git a/nym-wallet/src/pages/auth/pages/signin-mnemonic.tsx b/nym-wallet/src/pages/auth/pages/signin-mnemonic.tsx index a5babd3a96..a4f71ebd93 100644 --- a/nym-wallet/src/pages/auth/pages/signin-mnemonic.tsx +++ b/nym-wallet/src/pages/auth/pages/signin-mnemonic.tsx @@ -28,7 +28,7 @@ export const SignInMnemonic = () => { }, []); return ( - <Stack spacing={2} alignItems="center" minWidth="50%"> + <Stack spacing={2} alignItems="stretch" sx={{ width: '100%' }}> <Subtitle subtitle="Enter a mnemonic to sign in" /> <FormControl fullWidth> <form diff --git a/nym-wallet/src/pages/auth/pages/signin-password.tsx b/nym-wallet/src/pages/auth/pages/signin-password.tsx index 45e9c4602b..6b5b6e5b51 100644 --- a/nym-wallet/src/pages/auth/pages/signin-password.tsx +++ b/nym-wallet/src/pages/auth/pages/signin-password.tsx @@ -11,7 +11,7 @@ export const SignInPassword = () => { const navigate = useNavigate(); return ( - <Stack spacing={2} alignItems="center" minWidth="50%"> + <Stack spacing={2} alignItems="stretch" sx={{ width: '100%' }}> <Subtitle subtitle="Enter a password to sign in" /> <FormControl fullWidth> <form diff --git a/nym-wallet/src/pages/auth/pages/verify-mnemonic.tsx b/nym-wallet/src/pages/auth/pages/verify-mnemonic.tsx index cc654e2af2..15c0f9847d 100644 --- a/nym-wallet/src/pages/auth/pages/verify-mnemonic.tsx +++ b/nym-wallet/src/pages/auth/pages/verify-mnemonic.tsx @@ -40,22 +40,32 @@ export const VerifyMnemonic = () => { if (randomWords && hiddenRandomWords) { return ( - <> + <Stack spacing={3} sx={{ width: '100%', alignItems: 'stretch' }}> <Title title="Verify your mnemonic" /> - <Subtitle subtitle="Select the words from your mnmonic based on their order" /> + <Subtitle subtitle="Select the words from your mnemonic in the order shown above" /> <HiddenWords mnemonicWords={hiddenRandomWords} /> <WordTiles mnemonicWords={randomWords} onClick={currentSelection !== numberOfRandomWords ? revealWord : undefined} buttons /> - <Stack spacing={3} sx={{ width: 300 }}> + <Stack + spacing={2} + sx={{ + width: '100%', + maxWidth: 400, + alignSelf: 'center', + pt: { xs: 3, sm: 4 }, + mt: { xs: 0.5, sm: 1 }, + }} + > <Button variant="contained" fullWidth size="large" disabled={currentSelection !== numberOfRandomWords} onClick={() => navigate('/create-password')} + sx={{ fontWeight: 700 }} > Next </Button> @@ -63,7 +73,7 @@ export const VerifyMnemonic = () => { Back </Button> </Stack> - </> + </Stack> ); } return null; diff --git a/nym-wallet/src/pages/auth/pages/welcome.tsx b/nym-wallet/src/pages/auth/pages/welcome.tsx index e5bb19a264..d2956c912f 100644 --- a/nym-wallet/src/pages/auth/pages/welcome.tsx +++ b/nym-wallet/src/pages/auth/pages/welcome.tsx @@ -2,20 +2,19 @@ import React from 'react'; import { Button, Stack } from '@mui/material'; import { useNavigate } from 'react-router-dom'; -import { SubtitleSlick, Title } from '../components'; +import { SubtitleSlick } from '../components'; export const WelcomeContent: FCWithChildren<{}> = () => { const navigate = useNavigate(); return ( - <> - <Title title="Welcome to NYM" /> + <Stack spacing={1.5} sx={{ width: '100%', alignItems: 'stretch' }}> <SubtitleSlick subtitle="Next generation of privacy" /> - <Stack spacing={3} minWidth={300}> + <Stack spacing={2} sx={{ width: '100%', pt: 0.5 }}> <Button fullWidth color="primary" - sx={{ color: 'primary.contrastText', fontSize: 16 }} + sx={{ color: 'primary.contrastText', fontSize: 16, py: 1.25 }} variant="contained" size="large" onClick={() => navigate('/existing-account')} @@ -25,14 +24,14 @@ export const WelcomeContent: FCWithChildren<{}> = () => { <Button fullWidth color="inherit" - sx={{ fontSize: 16, color: '#FFFFFF' }} - disableElevation + sx={{ fontSize: 16, border: '1px solid', borderColor: 'divider' }} + variant="outlined" size="large" onClick={() => navigate('/create-mnemonic')} > Create account </Button> </Stack> - </> + </Stack> ); }; diff --git a/nym-wallet/src/pages/balance/Balance.tsx b/nym-wallet/src/pages/balance/Balance.tsx index 9ce2dcee80..62b0c60dee 100644 --- a/nym-wallet/src/pages/balance/Balance.tsx +++ b/nym-wallet/src/pages/balance/Balance.tsx @@ -1,11 +1,19 @@ import React from 'react'; -import { Alert, Grid, Typography, Skeleton } from '@mui/material'; +import { Alert, Box, Skeleton, Stack, Typography } from '@mui/material'; +import { alpha } from '@mui/material/styles'; +import { Balance, decimalToFloatApproximation } from '@nymproject/types'; import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { ClientAddress } from '@nymproject/react/client-address/ClientAddress'; import { Network } from 'src/types'; -import { Balance } from '@nymproject/types'; import { NymCard } from '../../components'; import { urls } from '../../context/main'; +import { useNymUsdPrice } from '../../hooks/useNymUsdPrice'; + +const usdFormatter = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + maximumFractionDigits: 2, +}); export const BalanceCard = ({ userBalance, @@ -19,49 +27,134 @@ export const BalanceCard = ({ network?: Network; clientAddress?: string; isLoading?: boolean; -}) => ( - <NymCard - title="Balance" - data-testid="check-balance" - borderless - Action={clientAddress && <ClientAddress address={clientAddress} withCopy showEntireAddress />} - > - <Grid container direction="column" spacing={2}> - <Grid item> - {userBalanceError && ( - <Alert severity="error" data-testid="error-refresh" sx={{ p: 2 }}> - {userBalanceError} - </Alert> - )} - {isLoading ? ( - <Skeleton width={160} height={42} /> - ) : ( - !userBalanceError && ( - <Typography - data-testid="refresh-success" +}) => { + const { usdPerNym, loading: priceLoading } = useNymUsdPrice(network); + + const nymFloat = + userBalance?.amount?.amount && userBalance.amount.amount.length > 0 + ? decimalToFloatApproximation(userBalance.amount.amount) + : undefined; + + const usdApproxLabel = + usdPerNym !== undefined && nymFloat !== undefined ? usdFormatter.format(nymFloat * usdPerNym) : undefined; + + const showUsdRow = Boolean(userBalance?.amount?.amount && userBalance.amount.amount.length > 0); + + let usdApproximationRow: React.ReactNode = null; + if (showUsdRow) { + if (priceLoading) { + usdApproximationRow = <Skeleton width={140} height={22} sx={{ mt: 0.5 }} />; + } else if (usdApproxLabel) { + usdApproximationRow = ( + <Typography + variant="body2" + sx={{ color: 'nym.text.muted', fontWeight: 500, mt: 0.25 }} + data-testid="balance-usd-approx" + > + {`≈ ${usdApproxLabel}`} + </Typography> + ); + } + } + + return ( + <NymCard + title="Balance" + subheader="Your primary spendable NYM balance" + dataTestid="check-balance" + borderless + Action={clientAddress && <ClientAddress address={clientAddress} withCopy showEntireAddress />} + sx={{ + backgroundColor: 'background.paper', + }} + > + <Stack spacing={2.5}> + <Box> + {userBalanceError && ( + <Alert severity="error" data-testid="error-refresh" sx={{ p: 2 }}> + {userBalanceError} + </Alert> + )} + {isLoading ? ( + <Skeleton width={160} height={42} /> + ) : ( + !userBalanceError && ( + <Stack spacing={1}> + <Typography + variant="caption" + sx={{ color: 'nym.text.muted', textTransform: 'uppercase', letterSpacing: 1 }} + > + Available now + </Typography> + <Typography + data-testid="refresh-success" + sx={{ + color: 'text.primary', + textTransform: 'uppercase', + fontWeight: 700, + fontSize: { xs: 30, md: 40 }, + lineHeight: 1, + }} + variant="h5" + > + {userBalance?.printable_balance || '-'} + </Typography> + {usdApproximationRow} + </Stack> + ) + )} + </Box> + {network && clientAddress && ( + <Box + sx={{ + display: 'flex', + flexDirection: { xs: 'column', sm: 'row' }, + gap: { xs: 1.5, sm: 2 }, + py: 1.5, + px: 2, + borderRadius: 3, + border: '1px solid', + borderColor: 'divider', + bgcolor: (t) => alpha(t.palette.text.primary, t.palette.mode === 'dark' ? 0.04 : 0.06), + }} + > + <Box sx={{ flex: 1, minWidth: 0 }}> + <Typography + variant="caption" + sx={{ color: 'nym.text.muted', textTransform: 'uppercase', letterSpacing: 0.8 }} + > + Destination + </Typography> + <Typography sx={{ mt: 0.5, fontWeight: 600, fontSize: 14 }}>Main account</Typography> + </Box> + <Box sx={{ - color: 'text.primary', - textTransform: 'uppercase', - fontWeight: '600', - fontSize: 28, + display: { xs: 'none', sm: 'block' }, + width: '1px', + alignSelf: 'stretch', + bgcolor: 'divider', + flexShrink: 0, }} - variant="h5" - > - {userBalance?.printable_balance || '—'} - </Typography> - ) + /> + <Box sx={{ flex: 1, minWidth: 0 }}> + <Typography + variant="caption" + sx={{ color: 'nym.text.muted', textTransform: 'uppercase', letterSpacing: 0.8 }} + > + Activity + </Typography> + <Box sx={{ mt: 0.5 }}> + <Link + href={`${urls(network).mixnetExplorer}account/${clientAddress}`} + target="_blank" + text="View latest transactions" + fontSize={14} + /> + </Box> + </Box> + </Box> )} - </Grid> - {network && ( - <Grid item> - <Link - href={`${urls(network).mixnetExplorer}account/${clientAddress}`} - target="_blank" - text="Last transactions" - fontSize={14} - /> - </Grid> - )} - </Grid> - </NymCard> -); + </Stack> + </NymCard> + ); +}; diff --git a/nym-wallet/src/pages/balance/NetworkOverviewSection.tsx b/nym-wallet/src/pages/balance/NetworkOverviewSection.tsx new file mode 100644 index 0000000000..d997adf698 --- /dev/null +++ b/nym-wallet/src/pages/balance/NetworkOverviewSection.tsx @@ -0,0 +1,577 @@ +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import { format } from 'date-fns'; +import { ExpandMore } from '@mui/icons-material'; +import { + Accordion, + AccordionDetails, + AccordionSummary, + Box, + Collapse, + IconButton, + LinearProgress, + Stack, + Typography, +} from '@mui/material'; +import { alpha, useTheme } from '@mui/material/styles'; +import { Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts'; +import { AppContext } from 'src/context/main'; +import { + CurrentEpochWithEnd, + EpochRewardsData, + fetchCurrentEpoch, + fetchEpochRewards, + fetchNymPriceDeduped, + fetchPacketsAndStaking, + fetchTotalDelegationsCount, + formatCompactNumber, + getNetworkOverviewEndpoints, + NymTokenomics, + PacketsAndStakingPoint, +} from 'src/api/networkOverview'; + +const TRAFFIC_CHART_EXPANDED_KEY = 'nymWallet.networkOverview.trafficChartExpanded'; +const SECTION_EXPANDED_KEY = 'nymWallet.networkOverview.sectionExpanded'; + +function readSectionExpanded(): boolean { + try { + const v = localStorage.getItem(SECTION_EXPANDED_KEY); + if (v === null) { + return true; + } + return v === 'true'; + } catch { + return true; + } +} + +function readTrafficChartExpanded(): boolean { + try { + const v = localStorage.getItem(TRAFFIC_CHART_EXPANDED_KEY); + if (v === null) { + return true; + } + return v === 'true'; + } catch { + return true; + } +} + +function epochProgressPercent(epoch: CurrentEpochWithEnd): number { + const start = new Date(epoch.current_epoch_start).getTime(); + const end = new Date(epoch.current_epoch_end).getTime(); + const now = Date.now(); + if (end <= start) { + return 0; + } + const p = ((now - start) / (end - start)) * 100; + return Math.min(100, Math.max(0, p)); +} + +function mixnetTrafficHeadline(data: PacketsAndStakingPoint[]): { + packets: number; + volumeLabel: string; + pctChange: number | null; +} | null { + if (data.length < 3) { + return null; + } + const todaysData = data[data.length - 2]; + const yesterdaysData = data[data.length - 3]; + const noiseLast24H = todaysData.total_packets_sent + todaysData.total_packets_received; + const noisePrevious24H = yesterdaysData.total_packets_sent + yesterdaysData.total_packets_received; + const BYTES_PER_PACKET = (2413 + 386) / 2; + const totalBytes = noiseLast24H * BYTES_PER_PACKET; + const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; + let size = totalBytes; + let unitIndex = 0; + for (; size >= 1024 && unitIndex < units.length - 1; unitIndex += 1) { + size /= 1024; + } + const volumeLabel = `${size.toFixed(2)} ${units[unitIndex]}`; + let pctChange: number | null = null; + if (noisePrevious24H > 0) { + pctChange = Number.parseFloat((((noiseLast24H - noisePrevious24H) / noisePrevious24H) * 100).toFixed(2)); + } + return { packets: noiseLast24H, volumeLabel, pctChange }; +} + +export const NetworkOverviewSection: React.FC = () => { + const theme = useTheme(); + const { network } = useContext(AppContext); + const endpoints = useMemo(() => getNetworkOverviewEndpoints(network), [network]); + + const [sectionExpanded, setSectionExpanded] = useState(readSectionExpanded); + const [trafficChartExpanded, setTrafficChartExpanded] = useState(readTrafficChartExpanded); + + const [packets, setPackets] = useState<PacketsAndStakingPoint[] | undefined>(); + const [packetsErr, setPacketsErr] = useState<string | undefined>(); + + const [epoch, setEpoch] = useState<CurrentEpochWithEnd | undefined>(); + const [epochErr, setEpochErr] = useState<string | undefined>(); + + const [rewards, setRewards] = useState<EpochRewardsData | undefined>(); + const [rewardsErr, setRewardsErr] = useState<string | undefined>(); + + const [price, setPrice] = useState<NymTokenomics | undefined>(); + const [priceErr, setPriceErr] = useState<string | undefined>(); + + const [delegationsCount, setDelegationsCount] = useState<number | undefined>(); + const [delegationsErr, setDelegationsErr] = useState<string | undefined>(); + + const [epochProgress, setEpochProgress] = useState(0); + + const persistSectionExpanded = useCallback((v: boolean) => { + setSectionExpanded(v); + try { + localStorage.setItem(SECTION_EXPANDED_KEY, String(v)); + } catch { + /* ignore quota / private mode */ + } + }, []); + + const persistTrafficChartExpanded = useCallback((v: boolean) => { + setTrafficChartExpanded(v); + try { + localStorage.setItem(TRAFFIC_CHART_EXPANDED_KEY, String(v)); + } catch { + /* ignore quota / private mode */ + } + }, []); + + useEffect(() => { + let cancelled = false; + + const load = async () => { + setPacketsErr(undefined); + setEpochErr(undefined); + setRewardsErr(undefined); + setPriceErr(undefined); + setDelegationsErr(undefined); + + const pStats = fetchPacketsAndStaking(endpoints.mixnodeStats) + .then((d) => { + if (!cancelled) { + setPackets(d); + } + }) + .catch(() => { + if (!cancelled) { + setPacketsErr('Could not load mixnet stats'); + } + }); + + const pEpoch = fetchCurrentEpoch(endpoints.epochCurrent) + .then((d) => { + if (!cancelled) { + setEpoch(d); + } + }) + .catch(() => { + if (!cancelled) { + setEpochErr('Could not load epoch'); + } + }); + + const pRewards = fetchEpochRewards(endpoints.epochRewards) + .then((d) => { + if (!cancelled) { + setRewards(d); + } + }) + .catch(() => { + if (!cancelled) { + setRewardsErr('Could not load reward params'); + } + }); + + const pPrice = fetchNymPriceDeduped(endpoints.nymPrice) + .then((d) => { + if (!cancelled) { + setPrice(d); + } + }) + .catch(() => { + if (!cancelled) { + setPriceErr('Could not load price'); + } + }); + + const pDeleg = fetchTotalDelegationsCount(endpoints.observatoryNodesBase) + .then((n) => { + if (!cancelled) { + setDelegationsCount(n); + } + }) + .catch(() => { + if (!cancelled) { + setDelegationsErr('Could not load delegations count'); + } + }); + + await Promise.all([pStats, pEpoch, pRewards, pPrice, pDeleg]); + }; + + load().catch(() => { + /* errors handled per-request */ + }); + return () => { + cancelled = true; + }; + }, [endpoints]); + + useEffect(() => { + if (!epoch) { + setEpochProgress(0); + return undefined; + } + const tick = () => { + setEpochProgress(epochProgressPercent(epoch)); + }; + tick(); + const id = window.setInterval(tick, 30_000); + return () => window.clearInterval(id); + }, [epoch]); + + const trafficHeadline = useMemo(() => (packets ? mixnetTrafficHeadline(packets) : null), [packets]); + + const trafficChartData = useMemo(() => { + if (!packets?.length) { + return []; + } + return packets + .slice(0, -1) + .map((item) => ({ + label: item.date_utc, + y: item.total_packets_sent + item.total_packets_received, + })) + .filter((row) => row.y >= 2_500_000_000); + }, [packets]); + + const tvlUsd = useMemo(() => { + if (!rewards || !price || !packets?.length) { + return undefined; + } + const lastTotalStake = packets[packets.length - 1]?.total_stake ?? 0; + const poolNym = Number.parseFloat(rewards.interval.reward_pool) / 1_000_000; + const stakeNym = lastTotalStake / 1_000_000; + return (poolNym + stakeNym) * price.quotes.USD.price; + }, [rewards, price, packets]); + + const epochTimes = useMemo(() => { + if (!epoch) { + return { start: '', end: '' }; + } + return { + start: format(new Date(epoch.current_epoch_start), 'HH:mm:ss'), + end: format(new Date(epoch.current_epoch_end), 'HH:mm:ss'), + }; + }, [epoch]); + + const cardShell = (children: React.ReactNode) => ( + <Box + sx={{ + borderRadius: 2, + border: (t) => `1px solid ${t.palette.divider}`, + bgcolor: (t) => + t.palette.mode === 'dark' ? 'nym.nymWallet.nav.background' : 'nym.nymWallet.background.subtle', + p: 2, + }} + > + {children} + </Box> + ); + + const subtle = theme.palette.text.secondary; + const trafficColor = '#8482FD'; + + const mixnetTrafficBody = () => { + if (packetsErr) { + return ( + <Typography color="error" variant="body2" sx={{ mt: 1 }}> + {packetsErr} + </Typography> + ); + } + if (!trafficHeadline) { + return ( + <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> + Loading… + </Typography> + ); + } + return ( + <> + <Stack direction="row" alignItems="baseline" gap={1} flexWrap="wrap" sx={{ mt: 0.5 }}> + <Typography variant="h5" sx={{ fontWeight: 700 }}> + {formatCompactNumber(trafficHeadline.packets)} + </Typography> + <Typography variant="h6" sx={{ color: trafficColor, fontWeight: 600 }}> + ({trafficHeadline.volumeLabel}) + </Typography> + </Stack> + {trafficHeadline.pctChange !== null ? ( + <Typography + variant="body2" + sx={{ + mt: 0.5, + color: trafficHeadline.pctChange >= 0 ? 'success.main' : 'error.main', + fontWeight: 600, + }} + > + {trafficHeadline.pctChange >= 0 ? '\u2191 ' : '\u2193 '} + {Math.abs(trafficHeadline.pctChange)}% (24h) + </Typography> + ) : null} + </> + ); + }; + + const epochBody = () => { + if (epochErr) { + return ( + <Typography color="error" variant="body2"> + {epochErr} + </Typography> + ); + } + if (!epoch) { + return ( + <Typography variant="body2" color="text.secondary"> + Loading… + </Typography> + ); + } + return ( + <> + <Typography variant="h5" sx={{ fontWeight: 700, fontFamily: 'monospace' }}> + {epoch.current_epoch_id} + </Typography> + <LinearProgress + variant="determinate" + value={epochProgress} + sx={{ + mt: 1, + height: 8, + borderRadius: 4, + bgcolor: alpha(theme.palette.primary.main, 0.15), + }} + /> + <Stack direction="row" justifyContent="space-between" sx={{ mt: 0.75 }}> + <Typography variant="caption" color="text.secondary" sx={{ fontFamily: 'monospace' }}> + Start {epochTimes.start} + </Typography> + <Typography variant="caption" color="text.secondary" sx={{ fontFamily: 'monospace' }}> + End {epochTimes.end} + </Typography> + </Stack> + </> + ); + }; + + const tokenomicsBody = () => { + if (priceErr || rewardsErr) { + return ( + <Typography color="error" variant="body2" sx={{ mt: 1 }}> + {priceErr ?? rewardsErr} + </Typography> + ); + } + if (!price) { + return ( + <Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}> + Loading… + </Typography> + ); + } + return ( + <Stack spacing={1.25} sx={{ mt: 1 }}> + <Stack direction="row" justifyContent="space-between" alignItems="baseline"> + <Typography fontWeight={700}>NYM</Typography> + <Typography variant="h6" sx={{ fontWeight: 700, fontFamily: 'monospace' }}> + ${price.quotes.USD.price.toFixed(4)} + </Typography> + </Stack> + <Box sx={{ borderTop: (t) => `1px solid ${t.palette.divider}`, pt: 1 }}> + <Stack direction="row" justifyContent="space-between"> + <Typography variant="body2" color="text.secondary"> + Market cap + </Typography> + <Typography variant="body2" sx={{ fontFamily: 'monospace' }}> + ${formatCompactNumber(price.quotes.USD.market_cap)} + </Typography> + </Stack> + </Box> + <Stack direction="row" justifyContent="space-between"> + <Typography variant="body2" color="text.secondary"> + 24h vol + </Typography> + <Typography variant="body2" sx={{ fontFamily: 'monospace' }}> + ${formatCompactNumber(price.quotes.USD.volume_24h)} + </Typography> + </Stack> + <Stack direction="row" justifyContent="space-between"> + <Typography variant="body2" color="text.secondary"> + TVL + </Typography> + <Typography variant="body2" sx={{ fontFamily: 'monospace' }}> + {tvlUsd !== undefined ? `$${formatCompactNumber(tvlUsd)}` : '…'} + </Typography> + </Stack> + </Stack> + ); + }; + + const showTrafficChartToggle = trafficChartData.length > 0; + + return ( + <Accordion + expanded={sectionExpanded} + onChange={(_, expanded) => persistSectionExpanded(expanded)} + elevation={0} + disableGutters + sx={{ + width: '100%', + minWidth: 0, + borderRadius: 2, + border: (t) => `1px solid ${t.palette.divider}`, + bgcolor: 'background.paper', + '&:before': { + display: 'none', + }, + }} + > + <AccordionSummary + expandIcon={<ExpandMore sx={{ color: 'text.secondary' }} />} + aria-controls="network-overview-panel" + id="network-overview-header" + sx={{ + px: 2, + minHeight: 56, + '& .MuiAccordionSummary-content': { + my: 1, + }, + }} + > + <Typography variant="h6" component="h2"> + Network overview + </Typography> + </AccordionSummary> + <AccordionDetails id="network-overview-panel" sx={{ px: 2, pb: 2, pt: 0 }}> + <Box + sx={{ + display: 'grid', + gridTemplateColumns: { + xs: '1fr', + md: 'repeat(2, minmax(0, 1fr))', + lg: 'repeat(3, minmax(0, 1fr))', + }, + gap: 2, + }} + > + {cardShell( + <> + <Stack direction="row" alignItems="flex-start" justifyContent="space-between" gap={1}> + <Typography variant="caption" color="text.secondary" sx={{ letterSpacing: 0.06, fontWeight: 600 }}> + Mixnet traffic + </Typography> + {showTrafficChartToggle ? ( + <IconButton + size="small" + aria-expanded={trafficChartExpanded} + aria-label={trafficChartExpanded ? 'Hide mixnet traffic chart' : 'Show mixnet traffic chart'} + onClick={() => persistTrafficChartExpanded(!trafficChartExpanded)} + sx={{ + mt: -0.5, + mr: -0.5, + color: 'text.secondary', + '&:hover': { color: 'primary.main' }, + }} + > + <ExpandMore + sx={{ + transform: trafficChartExpanded ? 'rotate(180deg)' : 'rotate(0deg)', + transition: theme.transitions.create('transform', { + duration: theme.transitions.duration.shortest, + }), + }} + /> + </IconButton> + ) : null} + </Stack> + {mixnetTrafficBody()} + <Collapse in={trafficChartExpanded && showTrafficChartToggle} timeout="auto" unmountOnExit> + <Box sx={{ height: 140, mt: 1, width: '100%', minWidth: 0 }}> + <ResponsiveContainer width="100%" height="100%"> + <LineChart data={trafficChartData} margin={{ top: 4, right: 8, left: 0, bottom: 0 }}> + <XAxis + dataKey="label" + tick={{ fontSize: 10, fill: subtle }} + tickFormatter={(v) => format(new Date(v), 'MMM d')} + /> + <YAxis + tick={{ fontSize: 10, fill: subtle }} + width={40} + tickFormatter={(v) => formatCompactNumber(Number(v))} + /> + <Tooltip + formatter={(value: number) => [formatCompactNumber(value), 'Packets']} + labelFormatter={(v) => format(new Date(v), 'PP')} + /> + <Line + type="monotone" + dataKey="y" + stroke={trafficColor} + strokeWidth={2} + dot={false} + isAnimationActive={false} + /> + </LineChart> + </ResponsiveContainer> + </Box> + </Collapse> + </>, + )} + + {cardShell( + <> + <Typography variant="caption" color="text.secondary" sx={{ letterSpacing: 0.06, fontWeight: 600 }}> + Delegations and epoch + </Typography> + <Stack spacing={1.5} sx={{ mt: 1 }}> + <Box> + <Typography variant="caption" color="text.secondary"> + Number of delegations + </Typography> + {delegationsErr ? ( + <Typography color="error" variant="body2"> + {delegationsErr} + </Typography> + ) : ( + <Typography variant="h5" sx={{ fontWeight: 700 }}> + {delegationsCount !== undefined ? formatCompactNumber(delegationsCount) : '…'} + </Typography> + )} + </Box> + <Box> + <Typography variant="caption" color="text.secondary"> + Current epoch + </Typography> + {epochBody()} + </Box> + </Stack> + </>, + )} + + {cardShell( + <> + <Typography variant="caption" color="text.secondary" sx={{ letterSpacing: 0.06, fontWeight: 600 }}> + Tokenomics + </Typography> + {tokenomicsBody()} + </>, + )} + </Box> + </AccordionDetails> + </Accordion> + ); +}; diff --git a/nym-wallet/src/pages/balance/OverviewQuickActions.tsx b/nym-wallet/src/pages/balance/OverviewQuickActions.tsx new file mode 100644 index 0000000000..e1eda59fc0 --- /dev/null +++ b/nym-wallet/src/pages/balance/OverviewQuickActions.tsx @@ -0,0 +1,96 @@ +import React, { useContext } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Box, Button, Stack, Typography } from '@mui/material'; +import QrCode2Icon from '@mui/icons-material/QrCode2'; +import SendIcon from '@mui/icons-material/Send'; +import TollIcon from '@mui/icons-material/Toll'; +import { alpha, useTheme, type Theme } from '@mui/material/styles'; +import { AppContext } from '../../context/main'; + +const iconWrapSx = (theme: Theme) => ({ + width: 40, + height: 40, + borderRadius: 2, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + bgcolor: alpha(theme.palette.primary.main, 0.12), + color: 'primary.main', + flexShrink: 0, +}); + +export const OverviewQuickActions = () => { + const theme = useTheme(); + const navigate = useNavigate(); + const { handleShowReceiveModal, handleShowSendModal } = useContext(AppContext); + + const panelSx = { + p: 2.5, + borderRadius: 3, + border: '1px solid', + borderColor: 'divider', + bgcolor: 'background.paper', + transition: 'border-color 0.2s, box-shadow 0.2s', + flex: 1, + minWidth: 0, + display: 'flex', + flexDirection: 'column', + '&:hover': { + borderColor: alpha(theme.palette.primary.main, 0.45), + boxShadow: theme.palette.nym.nymWallet.shadows.light, + }, + } as const; + + const pill = (icon: React.ReactNode, title: string, blurb: string, cta: React.ReactNode) => ( + <Box sx={panelSx}> + <Stack spacing={1.5} sx={{ height: '100%' }}> + <Stack direction="row" spacing={1.5} alignItems="flex-start"> + <Box sx={iconWrapSx(theme)}>{icon}</Box> + <Box sx={{ minWidth: 0 }}> + <Typography variant="subtitle2" fontWeight={600}> + {title} + </Typography> + <Typography variant="body2" sx={{ color: 'nym.text.muted', lineHeight: 1.35 }}> + {blurb} + </Typography> + </Box> + </Stack> + <Box sx={{ mt: 'auto', pt: 0.5 }}>{cta}</Box> + </Stack> + </Box> + ); + + return ( + <Stack spacing={1.5} sx={{ width: '100%' }}> + <Typography variant="caption" sx={{ color: 'nym.text.muted', textTransform: 'uppercase', letterSpacing: 1 }}> + Quick actions + </Typography> + <Stack direction={{ xs: 'column', md: 'row' }} spacing={2} alignItems="stretch"> + {pill( + <QrCode2Icon fontSize="small" />, + 'Receive', + 'QR and address for incoming NYM', + <Button variant="contained" fullWidth onClick={handleShowReceiveModal} sx={{ py: 1.25 }}> + Show QR and address + </Button>, + )} + {pill( + <SendIcon fontSize="small" />, + 'Send', + 'Transfer NYM to another address', + <Button variant="outlined" fullWidth onClick={handleShowSendModal} sx={{ py: 1.25 }}> + Send tokens + </Button>, + )} + {pill( + <TollIcon fontSize="small" />, + 'Buy', + 'See where to get NYM tokens', + <Button variant="outlined" fullWidth onClick={() => navigate('/buy')} sx={{ py: 1.25 }}> + Browse exchanges + </Button>, + )} + </Stack> + </Stack> + ); +}; diff --git a/nym-wallet/src/pages/balance/Vesting.tsx b/nym-wallet/src/pages/balance/Vesting.tsx index a27b70ca16..acb61dd5bc 100644 --- a/nym-wallet/src/pages/balance/Vesting.tsx +++ b/nym-wallet/src/pages/balance/Vesting.tsx @@ -21,7 +21,7 @@ export const VestingCard = ({ unlockedRewards?: string; unlockedTransferable?: string; originalVesting?: OriginalVestingResponse; - fetchTokenAllocation: () => Promise<void>; + fetchTokenAllocation: (isBackgroundPoll?: boolean) => Promise<void>; fetchBalance: () => Promise<void>; onTransfer: () => Promise<void>; isLoading?: boolean; @@ -50,6 +50,9 @@ export const VestingCard = ({ } borderless data-testid="check-unvested-tokens" + sx={{ + backgroundColor: 'background.paper', + }} Action={ <IconButton onClick={async () => { diff --git a/nym-wallet/src/pages/balance/index.tsx b/nym-wallet/src/pages/balance/index.tsx index 795cd2d0aa..2444f5aa34 100644 --- a/nym-wallet/src/pages/balance/index.tsx +++ b/nym-wallet/src/pages/balance/index.tsx @@ -1,11 +1,13 @@ import React, { useContext, useEffect, useState } from 'react'; -import { Box } from '@mui/material'; +import { Stack } from '@mui/material'; import { AppContext } from '../../context/main'; import { BalanceCard } from './Balance'; import { VestingCard } from './Vesting'; import { PageLayout } from '../../layouts'; import { TransferModal } from '../../components/Balance/modals/TransferModal'; +import { OverviewQuickActions } from './OverviewQuickActions'; +import { NetworkOverviewSection } from './NetworkOverviewSection'; export const Balance = () => { const [showTransferModal, setShowTransferModal] = useState(false); @@ -15,7 +17,7 @@ export const Balance = () => { useEffect(() => { const interval = setInterval(() => { userBalance.fetchBalance(); - userBalance.fetchTokenAllocation(); + userBalance.fetchTokenAllocation(true); }, 10000); return () => clearInterval(interval); @@ -30,13 +32,17 @@ export const Balance = () => { return ( <PageLayout> - <Box display="flex" flexDirection="column" gap={4}> - <BalanceCard - userBalance={userBalance.balance} - userBalanceError={userBalance.error} - clientAddress={clientDetails?.client_address} - network={network} - /> + <Stack spacing={3.5}> + <Stack spacing={3} sx={{ width: '100%', minWidth: 0 }}> + <BalanceCard + userBalance={userBalance.balance} + userBalanceError={userBalance.error} + clientAddress={clientDetails?.client_address} + network={network} + /> + <OverviewQuickActions /> + {network === 'MAINNET' ? <NetworkOverviewSection /> : null} + </Stack> <VestingCard unlockedTokens={appendDenom(userBalance.tokenAllocation?.spendableVestedCoins)} unlockedRewards={appendDenom(userBalance.tokenAllocation?.spendableRewardCoins)} @@ -47,7 +53,7 @@ export const Balance = () => { fetchTokenAllocation={userBalance.fetchTokenAllocation} /> {showTransferModal && <TransferModal onClose={() => setShowTransferModal(false)} />} - </Box> + </Stack> </PageLayout> ); }; diff --git a/nym-wallet/src/pages/bonding/Bonding.tsx b/nym-wallet/src/pages/bonding/Bonding.tsx index a5b2d9f5c5..f4765fa5dc 100644 --- a/nym-wallet/src/pages/bonding/Bonding.tsx +++ b/nym-wallet/src/pages/bonding/Bonding.tsx @@ -20,6 +20,7 @@ import { BondedNymNode } from 'src/components/Bonding/BondedNymNode'; import { UpdateBondAmountNymNode } from 'src/components/Bonding/modals/UpdateBondAmountNymNode'; import { BondNymNode } from 'src/components/Bonding/modals/BondNymNodeModal'; import { BondingContextProvider, useBondingContext } from '../../context'; +import { PageLayout } from '../../layouts'; export const Bonding = () => { const [showModal, setShowModal] = useState< @@ -208,125 +209,129 @@ export const Bonding = () => { } return ( - <Box sx={{ mt: 4 }}> - {bondedNode && !isNymNode(bondedNode) && bondedNode?.proxy && ( - <Alert severity="warning" sx={{ mb: 3 }}> - <AlertTitle sx={{ fontWeight: 600 }}>Your bonded node is using tokens from the vesting contract!</AlertTitle> - <Typography> - In order to claim your rewards, you will need to migrate it out of the vesting contract.{' '} - </Typography> - <Typography mt={1}> - <strong>Never fear</strong>, if you do not migrate them, <strong>you will continue to get rewards</strong>. - However, please migrate your bonded node as soon as possible. - </Typography> - <Button variant="contained" size="small" sx={{ mt: 1 }} onClick={() => setShowMigrationModal(true)}> - Migrate now - </Button> - </Alert> - )} + <PageLayout maxWidth={bondedNode ? 'wide' : 'narrow'}> + <Box> + {bondedNode && !isNymNode(bondedNode) && bondedNode?.proxy && ( + <Alert severity="warning" sx={{ mb: 3 }}> + <AlertTitle sx={{ fontWeight: 600 }}> + Your bonded node is using tokens from the vesting contract! + </AlertTitle> + <Typography> + In order to claim your rewards, you will need to migrate it out of the vesting contract.{' '} + </Typography> + <Typography mt={1}> + <strong>Never fear</strong>, if you do not migrate them, <strong>you will continue to get rewards</strong> + . However, please migrate your bonded node as soon as possible. + </Typography> + <Button variant="contained" size="small" sx={{ mt: 1 }} onClick={() => setShowMigrationModal(true)}> + Migrate now + </Button> + </Alert> + )} - <VestingWarningModal - kind="bond" - isVisible={showMigrationModal} - handleClose={() => { - setShowMigrationModal(false); - }} - handleMigrate={async () => { - await handleMigrateVestedMixnode(); - }} - /> - - <MigrateLegacyNode - open={showMigrateLegacyNodeModal} - onClose={() => setShowMigrateLegacyNodeModal(false)} - handleMigrate={handleMigrateLegacyNode} - /> - - {!bondedNode && <Bond disabled={isLoading} onBond={() => setShowModal('bond-nymnode')} />} - - {bondedNode && isNymNode(bondedNode) && ( - <BondedNymNode - nymnode={bondedNode} - network={network} - onActionSelect={(action) => handleBondedNymNodeAction(action)} - /> - )} - - {bondedNode && isMixnode(bondedNode) && ( - <BondedMixnode - mixnode={bondedNode} - network={network} - onShowMigrateToNymNodeModal={() => setShowMigrateLegacyNodeModal(true)} - onActionSelect={(action) => handleBondedMixnodeAction(action)} - /> - )} - - {bondedNode && isGateway(bondedNode) && ( - <BondedGateway - gateway={bondedNode} - network={network} - onShowMigrateToNymNodeModal={() => setShowMigrateLegacyNodeModal(true)} - onActionSelect={handleBondedMixnodeAction} - /> - )} - - <BondNymNode open={showModal === 'bond-nymnode'} onClose={handleCloseModal} onBond={handleBondNymNode} /> - - {showModal === 'update-bond-oversaturated' && uncappedSaturation && ( - <BondOversaturatedModal - open - onClose={() => setShowModal(undefined)} - onContinue={() => setShowModal('update-bond')} - saturationPercentage={uncappedSaturation.toString()} - /> - )} - - {showModal === 'update-bond' && bondedNode && isMixnode(bondedNode) && ( - <UpdateBondAmountModal - node={bondedNode} - onUpdateBond={handleUpdateBond} - onClose={() => setShowModal(undefined)} - onError={handleError} - /> - )} - - {showModal === 'update-bond-nymnode' && bondedNode && isNymNode(bondedNode) && ( - <UpdateBondAmountNymNode - node={bondedNode} - onUpdateBond={handleUpdateBond} - onClose={() => setShowModal(undefined)} - onError={handleError} - /> - )} - - {showModal === 'redeem' && bondedNode && isNymNode(bondedNode) && ( - <RedeemRewardsModal - node={bondedNode} - onClose={() => setShowModal(undefined)} - onConfirm={handleRedeemReward} - onError={handleError} - /> - )} - - {confirmationDetails && confirmationDetails.status === 'success' && ( - <ConfirmationDetailsModal - title={confirmationDetails.title} - subtitle={confirmationDetails.subtitle} - status={confirmationDetails.status} - txUrl={confirmationDetails.txUrl} - onClose={() => { - setConfirmationDetails(undefined); - handleCloseModal(); + <VestingWarningModal + kind="bond" + isVisible={showMigrationModal} + handleClose={() => { + setShowMigrationModal(false); + }} + handleMigrate={async () => { + await handleMigrateVestedMixnode(); }} /> - )} - {confirmationDetails && confirmationDetails.status === 'error' && ( - <ErrorModal open message={confirmationDetails.subtitle} onClose={() => setConfirmationDetails(undefined)} /> - )} + <MigrateLegacyNode + open={showMigrateLegacyNodeModal} + onClose={() => setShowMigrateLegacyNodeModal(false)} + handleMigrate={handleMigrateLegacyNode} + /> - {isLoading && <LoadingModal />} - </Box> + {!bondedNode && <Bond disabled={isLoading} onBond={() => setShowModal('bond-nymnode')} />} + + {bondedNode && isNymNode(bondedNode) && ( + <BondedNymNode + nymnode={bondedNode} + network={network} + onActionSelect={(action) => handleBondedNymNodeAction(action)} + /> + )} + + {bondedNode && isMixnode(bondedNode) && ( + <BondedMixnode + mixnode={bondedNode} + network={network} + onShowMigrateToNymNodeModal={() => setShowMigrateLegacyNodeModal(true)} + onActionSelect={(action) => handleBondedMixnodeAction(action)} + /> + )} + + {bondedNode && isGateway(bondedNode) && ( + <BondedGateway + gateway={bondedNode} + network={network} + onShowMigrateToNymNodeModal={() => setShowMigrateLegacyNodeModal(true)} + onActionSelect={handleBondedMixnodeAction} + /> + )} + + <BondNymNode open={showModal === 'bond-nymnode'} onClose={handleCloseModal} onBond={handleBondNymNode} /> + + {showModal === 'update-bond-oversaturated' && uncappedSaturation && ( + <BondOversaturatedModal + open + onClose={() => setShowModal(undefined)} + onContinue={() => setShowModal('update-bond')} + saturationPercentage={uncappedSaturation.toString()} + /> + )} + + {showModal === 'update-bond' && bondedNode && isMixnode(bondedNode) && ( + <UpdateBondAmountModal + node={bondedNode} + onUpdateBond={handleUpdateBond} + onClose={() => setShowModal(undefined)} + onError={handleError} + /> + )} + + {showModal === 'update-bond-nymnode' && bondedNode && isNymNode(bondedNode) && ( + <UpdateBondAmountNymNode + node={bondedNode} + onUpdateBond={handleUpdateBond} + onClose={() => setShowModal(undefined)} + onError={handleError} + /> + )} + + {showModal === 'redeem' && bondedNode && isNymNode(bondedNode) && ( + <RedeemRewardsModal + node={bondedNode} + onClose={() => setShowModal(undefined)} + onConfirm={handleRedeemReward} + onError={handleError} + /> + )} + + {confirmationDetails && confirmationDetails.status === 'success' && ( + <ConfirmationDetailsModal + title={confirmationDetails.title} + subtitle={confirmationDetails.subtitle} + status={confirmationDetails.status} + txUrl={confirmationDetails.txUrl} + onClose={() => { + setConfirmationDetails(undefined); + handleCloseModal(); + }} + /> + )} + + {confirmationDetails && confirmationDetails.status === 'error' && ( + <ErrorModal open message={confirmationDetails.subtitle} onClose={() => setConfirmationDetails(undefined)} /> + )} + + {isLoading && <LoadingModal />} + </Box> + </PageLayout> ); }; diff --git a/nym-wallet/src/pages/bonding/BondingPage.stories.tsx b/nym-wallet/src/pages/bonding/BondingPage.stories.tsx deleted file mode 100644 index 1a2caa1f2e..0000000000 --- a/nym-wallet/src/pages/bonding/BondingPage.stories.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react'; -import { BondingPage } from './Bonding'; -import { MockBondingContextProvider } from '../../context/mocks/bonding'; - -export default { - title: 'Bonding/Flows/Mock', -}; - -export const Default = () => ( - <MockBondingContextProvider> - <BondingPage /> - </MockBondingContextProvider> -); diff --git a/nym-wallet/src/pages/bonding/node-settings/node-test/index.tsx b/nym-wallet/src/pages/bonding/node-settings/node-test/index.tsx index b9eeaf38d7..75203bc09e 100644 --- a/nym-wallet/src/pages/bonding/node-settings/node-test/index.tsx +++ b/nym-wallet/src/pages/bonding/node-settings/node-test/index.tsx @@ -21,13 +21,15 @@ export const NodeTestPage = () => { const [testDate, setTestDate] = useState<string>(); const testStateRef = useRef<TestStatus>('Stopped'); - const timerRef = useRef<NodeJS.Timeout>(); + const timerRef = useRef<NodeJS.Timeout | null>(null); const { network } = useContext(AppContext); const { bondedNode } = useBondingContext(); const handleTestTimeout = () => { - clearTimeout(timerRef.current); + if (timerRef.current) { + clearTimeout(timerRef.current); + } timerRef.current = setTimeout(() => { if (testStateRef.current === 'Running') { setIsLoading(false); @@ -78,7 +80,9 @@ export const NodeTestPage = () => { loadNodeTestClient(); return () => { - clearTimeout(timerRef.current); + if (timerRef.current) { + clearTimeout(timerRef.current); + } if (nodeTestClient) { nodeTestClient.tester.disconnectFromGateway(); nodeTestClient.terminate(); diff --git a/nym-wallet/src/pages/buy/index.stories.tsx b/nym-wallet/src/pages/buy/index.stories.tsx deleted file mode 100644 index d7ab6c7127..0000000000 --- a/nym-wallet/src/pages/buy/index.stories.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; - -import { Tutorial } from '../../components/Buy/Tutorial'; - -export default { - title: 'Buy/Page', - component: Tutorial, -}; - -export const BuyPage = () => <Tutorial />; diff --git a/nym-wallet/src/pages/buy/index.tsx b/nym-wallet/src/pages/buy/index.tsx index 90d359e269..4e752e6ce9 100644 --- a/nym-wallet/src/pages/buy/index.tsx +++ b/nym-wallet/src/pages/buy/index.tsx @@ -1,4 +1,12 @@ import React from 'react'; +import { Stack } from '@mui/material'; import { Tutorial } from 'src/components/Buy/Tutorial'; +import { PageLayout } from '../../layouts'; -export const BuyPage = () => <Tutorial />; +export const BuyPage = () => ( + <PageLayout> + <Stack spacing={3}> + <Tutorial /> + </Stack> + </PageLayout> +); diff --git a/nym-wallet/src/pages/delegation/DelegationPage.stories.tsx b/nym-wallet/src/pages/delegation/DelegationPage.stories.tsx deleted file mode 100644 index b243a59464..0000000000 --- a/nym-wallet/src/pages/delegation/DelegationPage.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import * as React from 'react'; -import { DelegationPage } from './index'; -import { MockDelegationContextProvider } from '../../context/mocks/delegations'; -import { MockRewardsContextProvider } from '../../context/mocks/rewards'; -import { MockMainContextProvider } from '../../context/mocks/main'; - -export default { - title: 'Delegation/Flows/Mock', -}; - -export const Default = () => ( - <MockMainContextProvider> - <MockDelegationContextProvider> - <MockRewardsContextProvider> - <DelegationPage isStorybook /> - </MockRewardsContextProvider> - </MockDelegationContextProvider> - </MockMainContextProvider> -); diff --git a/nym-wallet/src/pages/delegation/index.tsx b/nym-wallet/src/pages/delegation/index.tsx index 67af5247da..7b3abbfc54 100644 --- a/nym-wallet/src/pages/delegation/index.tsx +++ b/nym-wallet/src/pages/delegation/index.tsx @@ -1,6 +1,7 @@ import React, { FC, useContext, useEffect, useState } from 'react'; -import { Alert, AlertTitle, Box, Button, Paper, Stack, Typography } from '@mui/material'; -import { Theme, useTheme } from '@mui/material/styles'; +import { OpenInNew } from '@mui/icons-material'; +import { Alert, AlertTitle, Box, Button, CircularProgress, LinearProgress, Stack, Typography } from '@mui/material'; +import { alpha, useTheme } from '@mui/material/styles'; import { DecCoin, decimalToFloatApproximation, DelegationWithEverything, FeeDetails } from '@nymproject/types'; import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; import { AppContext, urls } from 'src/context/main'; @@ -11,7 +12,6 @@ import { OverSaturatedBlockerModal } from 'src/components/Delegation/DelegateBlo import { getSpendableCoins, migrateVestedDelegations, userBalance } from 'src/requests'; import { LoadingModal } from 'src/components/Modals/LoadingModal'; import { getIntervalAsDate, toPercentIntegerString } from 'src/utils'; -import { RewardsSummary } from '../../components/Rewards/RewardsSummary'; import { DelegationContextProvider, isDelegation, TDelegations, useDelegationContext } from '../../context/delegations'; import { RewardsContextProvider, useRewardsContext } from '../../context/rewards'; import { DelegateModal } from '../../components/Delegation/DelegateModal'; @@ -19,18 +19,10 @@ import { UndelegateModal } from '../../components/Delegation/UndelegateModal'; import { DelegationListItemActions } from '../../components/Delegation/DelegationActions'; import { RedeemModal } from '../../components/Rewards/RedeemModal'; import { DelegationModal, DelegationModalProps } from '../../components/Delegation/DelegationModal'; -import { backDropStyles, modalStyles } from '../../../.storybook/storiesStyles'; import { VestingWarningModal } from '../../components/VestingWarningModal'; +import { PageLayout } from '../../layouts'; -const storybookStyles = (theme: Theme, isStorybook?: boolean, backdropProps?: object) => - isStorybook - ? { - backdropProps: { ...backDropStyles(theme), ...backdropProps }, - sx: modalStyles(theme), - } - : {}; - -export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => { +export const Delegation: FC = () => { const [showNewDelegationModal, setShowNewDelegationModal] = useState<boolean>(false); const [showDelegateMoreModal, setShowDelegateMoreModal] = useState<boolean>(false); const [showUndelegateModal, setShowUndelegateModal] = useState<boolean>(false); @@ -51,8 +43,6 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => { const { delegations, - totalDelegations, - totalRewards, isLoading, addDelegation, undelegate, @@ -319,7 +309,15 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => { }; const delegationsComponent = (delegationItems: TDelegations | undefined) => { - if (delegationItems && Boolean(delegationItems?.length)) { + if (delegationItems === undefined) { + return ( + <Box sx={{ py: 10, display: 'flex', justifyContent: 'center', alignItems: 'center' }}> + <CircularProgress size={36} aria-label="Loading delegations" /> + </Box> + ); + } + + if (delegationItems.length > 0) { return ( <> {delegationsUseVestingTokens && ( @@ -356,9 +354,9 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => { )} <DelegationList explorerUrl={urls(network).networkExplorer} - isLoading={isLoading && !isActionModalOpen} items={delegationItems} onItemActionClick={handleDelegationItemActionClick} + nextEpoch={nextEpoch} /> </> ); @@ -395,71 +393,99 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => { ); }; - if (isLoading) { - return <LoadingModal />; - } - return ( <> - {/* Main container - make sure it constrains width properly */} - <Paper - elevation={0} - sx={{ - p: 3, - mt: 4, - maxWidth: '100%', - overflowX: 'hidden', - }} - > + <PageLayout> <Stack spacing={3}> - <Box display="flex" justifyContent="space-between"> - {' '} - <Box display="flex" flexDirection="column"> - <Typography variant="h6" lineHeight={1.334} fontWeight={600}> - Delegations - </Typography> + <Box + sx={{ + p: { xs: 2, md: 3 }, + maxWidth: '100%', + overflowX: 'hidden', + borderRadius: 4, + bgcolor: 'background.paper', + border: (t) => `1px solid ${t.palette.divider}`, + boxShadow: (t) => t.palette.nym.nymWallet.shadows.light, + }} + > + <Stack spacing={3}> {!!delegations?.length && ( - <Stack marginTop={1.5} gap={0.5} direction="row" alignItems="center"> - <Typography fontSize={14}>Select nodes to delegate to using the</Typography> - <Link - href={`${urls(network).networkExplorer}/nodes`} - target="_blank" - rel="noreferrer" - text="network Explorer" - fontSize={14} - fontWeight={theme.palette.mode === 'light' ? 400 : 600} - noIcon - /> + <Stack + direction={{ xs: 'column', sm: 'row' }} + alignItems={{ xs: 'stretch', sm: 'center' }} + justifyContent="space-between" + gap={2} + > + <Box + sx={{ + flex: 1, + minWidth: 0, + display: 'flex', + alignItems: 'flex-start', + gap: 1.25, + p: 1.75, + borderRadius: 2, + border: (t) => `1px solid ${t.palette.divider}`, + bgcolor: (t) => + t.palette.mode === 'dark' + ? alpha(t.palette.common.white, 0.04) + : alpha(t.palette.common.black, 0.04), + }} + > + <OpenInNew + sx={{ + fontSize: 20, + mt: 0.125, + color: 'primary.main', + flexShrink: 0, + }} + /> + <Typography fontSize={14} color="text.secondary" sx={{ lineHeight: 1.5 }}> + Select nodes using the{' '} + <Link + href={`${urls(network).networkExplorer}/nodes`} + target="_blank" + rel="noreferrer" + text="network explorer" + fontSize={14} + fontWeight={600} + noIcon + /> + . Compare performance and filters before you delegate. + </Typography> + </Box> + <Button + variant="contained" + disableElevation + onClick={() => setShowNewDelegationModal(true)} + sx={{ + py: 1.5, + px: 4, + color: 'primary.contrastText', + flexShrink: 0, + alignSelf: { xs: 'stretch', sm: 'center' }, + }} + > + New delegation + </Button> </Stack> )} - </Box> - {!!delegations?.length && ( - <Button - variant="contained" - disableElevation - onClick={() => setShowNewDelegationModal(true)} - sx={{ py: 1.5, px: 5, color: 'primary.contrastText', height: 'fit-content' }} - > - Delegate - </Button> - )} - </Box> - {!!delegations?.length && ( - <Box display="flex" justifyContent="space-between" alignItems="end"> - <RewardsSummary isLoading={false} totalDelegation={totalDelegations} totalRewards={totalRewards} /> - {nextEpoch instanceof Error ? null : ( - <Typography fontSize={14}> - Next epoch starts at <b>{nextEpoch}</b> - </Typography> + {isLoading && delegations !== undefined && !isActionModalOpen && ( + <LinearProgress + sx={{ + height: 3, + borderRadius: 3, + '& .MuiLinearProgress-bar': { borderRadius: 3 }, + }} + /> )} - </Box> - )} - {/* Add a container to ensure delegations are constrained */} - <Box sx={{ width: '100%', overflowX: 'hidden' }}>{delegationsComponent(delegations)}</Box> + <Box sx={{ width: '100%', overflowX: 'hidden' }}>{delegationsComponent(delegations)}</Box> + </Stack> + </Box> </Stack> - </Paper> + </PageLayout> {showNewDelegationModal && ( <DelegateModal @@ -472,7 +498,6 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => { accountBalance={balance?.printable_balance} rewardInterval="weekly" hasVestingContract={Boolean(originalVesting)} - {...storybookStyles(theme, isStorybook)} /> )} @@ -548,10 +573,10 @@ export const Delegation: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => { ); }; -export const DelegationPage: FC<{ isStorybook?: boolean }> = ({ isStorybook }) => ( +export const DelegationPage: FC = () => ( <DelegationContextProvider> <RewardsContextProvider> - <Delegation isStorybook={isStorybook} /> + <Delegation /> </RewardsContextProvider> </DelegationContextProvider> ); diff --git a/nym-wallet/src/pages/settings/AdvancedSettings.tsx b/nym-wallet/src/pages/settings/AdvancedSettings.tsx index 76ec15c967..e9684d43e4 100644 --- a/nym-wallet/src/pages/settings/AdvancedSettings.tsx +++ b/nym-wallet/src/pages/settings/AdvancedSettings.tsx @@ -15,7 +15,7 @@ const AdvancedSettings = () => { <Stack direction="row" justifyContent="space-between" padding={3}> <Stack direction="column" gap={1}> <Typography variant="h6">Terminal</Typography> - <Typography variant="caption" sx={{ color: 'nym.text.muted' }}> + <Typography variant="body2" sx={{ color: 'text.secondary' }}> Open the terminal (dev mode) </Typography> </Stack> @@ -31,7 +31,7 @@ const AdvancedSettings = () => { <Stack direction="row" justifyContent="space-between" padding={3}> <Stack direction="column" gap={1}> <Typography variant="h6">Logs</Typography> - <Typography variant="caption" sx={{ color: 'nym.text.muted' }}> + <Typography variant="body2" sx={{ color: 'text.secondary' }}> Open logs to monitor all actions in the wallet </Typography> </Stack> diff --git a/nym-wallet/src/pages/settings/GeneralSettings.tsx b/nym-wallet/src/pages/settings/GeneralSettings.tsx index 5cc88915a6..e0d08be691 100644 --- a/nym-wallet/src/pages/settings/GeneralSettings.tsx +++ b/nym-wallet/src/pages/settings/GeneralSettings.tsx @@ -4,14 +4,20 @@ import { AppVersion, ThemeSwitcher } from '../../components/Settings'; const GeneralSettings = () => ( <Box pb={3}> - <Stack direction="row" justifyContent="space-between" padding={3}> - <Stack direction="column" gap={1}> + <Stack + direction="row" + justifyContent="space-between" + alignItems="flex-start" + gap={2} + sx={{ padding: 3, pr: { xs: 3, sm: 4 } }} + > + <Stack direction="column" gap={1} sx={{ minWidth: 0 }}> <Typography variant="h6">Version</Typography> - <Typography variant="caption" sx={{ color: 'nym.text.muted' }}> - Current version of wallet and updates + <Typography variant="body2" sx={{ color: 'text.secondary' }}> + Installed build and update checks </Typography> </Stack> - <Box> + <Box sx={{ flexShrink: 0 }}> <AppVersion /> </Box> </Stack> @@ -19,7 +25,7 @@ const GeneralSettings = () => ( <Stack direction="row" justifyContent="space-between" padding={3}> <Stack direction="column" gap={1}> <Typography variant="h6">Theme</Typography> - <Typography variant="caption" sx={{ color: 'nym.text.muted' }}> + <Typography variant="body2" sx={{ color: 'text.secondary' }}> Select the theme </Typography> </Stack> diff --git a/nym-wallet/src/pages/settings/SecuritySettings.tsx b/nym-wallet/src/pages/settings/SecuritySettings.tsx index 190fa04204..d773ab13f5 100644 --- a/nym-wallet/src/pages/settings/SecuritySettings.tsx +++ b/nym-wallet/src/pages/settings/SecuritySettings.tsx @@ -44,12 +44,12 @@ const SecuritySettings = () => { )} {passwordExists ? ( - <Typography variant="caption" sx={{ color: 'nym.text.muted', maxWidth: '220px' }}> + <Typography variant="body2" sx={{ color: 'text.secondary', maxWidth: 480, lineHeight: 1.5 }}> Change your existing password. A strong password should have at least 8 characters, one capital letter, a number and a special character </Typography> ) : ( - <Typography variant="caption" sx={{ color: 'nym.text.muted', maxWidth: '220px' }}> + <Typography variant="body2" sx={{ color: 'text.secondary', maxWidth: 480, lineHeight: 1.5 }}> Create a strong password for your wallet. A strong password should have at least 8 characters, one capital letter, a number and a special character </Typography> diff --git a/nym-wallet/src/pages/settings/Settings.tsx b/nym-wallet/src/pages/settings/Settings.tsx index 42c53da887..0d47a7195a 100644 --- a/nym-wallet/src/pages/settings/Settings.tsx +++ b/nym-wallet/src/pages/settings/Settings.tsx @@ -21,9 +21,21 @@ const Settings = () => { return ( <PageLayout> + <Box sx={{ mb: 2 }}> + <Typography variant="overline" sx={{ color: 'text.secondary', letterSpacing: 1.2 }}> + Preferences + </Typography> + <Typography variant="h4" sx={{ mt: 0.5 }}> + Wallet settings + </Typography> + </Box> <NymCard borderless noPadding + sx={{ + borderRadius: 4, + backgroundColor: 'background.paper', + }} title={ <Stack gap={2} sx={{ py: 0 }}> <Box @@ -50,17 +62,23 @@ const Settings = () => { borderBottom: 'none', borderTop: 'none', '& button': { - p: 0, - mr: 4, + px: 2, + py: 1, + mr: 1.5, minWidth: 'none', - fontSize: 16, + fontSize: 15, + borderRadius: '999px', }, '& button:hover': { color: theme.palette.nym.highlight, opacity: 1, }, }} - tabIndicatorStyles={{ height: 4, bottom: '6px', borderRadius: '2px' }} + tabIndicatorStyles={{ + height: 3, + borderRadius: 3, + backgroundColor: theme.palette.primary.main, + }} /> </Box> </Stack> diff --git a/nym-wallet/src/pages/terminal/index.tsx b/nym-wallet/src/pages/terminal/index.tsx index d027e782b9..c994c89ac2 100644 --- a/nym-wallet/src/pages/terminal/index.tsx +++ b/nym-wallet/src/pages/terminal/index.tsx @@ -1,39 +1,85 @@ import React, { useContext, useState } from 'react'; -import { Alert, Box, Paper, Dialog, Typography } from '@mui/material'; +import { Alert, Box, Dialog, IconButton, Paper, Stack, Typography, alpha, useTheme } from '@mui/material'; import TerminalIcon from '@mui/icons-material/Terminal'; import RefreshIcon from '@mui/icons-material/Refresh'; import CloseIcon from '@mui/icons-material/Close'; -import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; -import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; +import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; +import UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; import { Console } from 'src/utils/console'; import { AppContext } from '../../context/main'; import { NymCard } from '../../components'; import { getCurrentInterval, getAllPendingDelegations, getMixNodeDelegationsForCurrentAccount } from '../../requests'; const TerminalSection: FCWithChildren<{ - heading: React.ReactNode; + heading: string; children: React.ReactNode; }> = ({ heading, children }) => { const [isCollapsed, setIsCollapsed] = useState<boolean>(true); - const toggleCollapse = () => { - setIsCollapsed((prev) => !prev); - }; - return ( - <Box mb={2}> - <Typography display="flex"> - {isCollapsed ? <ArrowDropDownIcon onClick={toggleCollapse} /> : <ArrowDropUpIcon onClick={toggleCollapse} />} - <Typography ml={2} fontWeight={600}> + <Box + mb={1.5} + sx={{ + borderRadius: 2, + border: (t) => `1px solid ${t.palette.divider}`, + overflow: 'hidden', + }} + > + <Stack + direction="row" + alignItems="center" + onClick={() => setIsCollapsed((prev) => !prev)} + sx={{ + px: 2, + py: 1.25, + cursor: 'pointer', + bgcolor: (t) => alpha(t.palette.primary.main, t.palette.mode === 'dark' ? 0.08 : 0.06), + '&:hover': { + bgcolor: (t) => alpha(t.palette.primary.main, t.palette.mode === 'dark' ? 0.12 : 0.09), + }, + }} + > + <IconButton size="small" edge="start" sx={{ mr: 0.5 }} aria-expanded={!isCollapsed}> + {isCollapsed ? <UnfoldMoreIcon fontSize="small" /> : <UnfoldLessIcon fontSize="small" />} + </IconButton> + <Typography variant="subtitle2" fontWeight={600} sx={{ flex: 1 }}> {heading} </Typography> - </Typography> - {!isCollapsed && <Paper sx={{ px: 3, py: 0.5 }}>{children}</Paper>} + </Stack> + {!isCollapsed && ( + <Paper + elevation={0} + sx={{ + px: 2, + py: 1.5, + borderRadius: 0, + bgcolor: 'background.default', + borderTop: (t) => `1px solid ${t.palette.divider}`, + }} + > + <Box + component="pre" + sx={{ + m: 0, + fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace', + fontSize: '0.75rem', + lineHeight: 1.5, + overflow: 'auto', + maxHeight: 280, + whiteSpace: 'pre-wrap', + wordBreak: 'break-word', + }} + > + {children} + </Box> + </Paper> + )} </Box> ); }; const TerminalInner: FCWithChildren = () => { + const theme = useTheme(); const { network, userBalance, clientDetails, handleShowTerminal, appEnv } = useContext(AppContext); const [mixnodeDelegations, setMixnodeDelegations] = useState<any>(); const [pendingEvents, setPendingEvents] = useState<any>(); @@ -83,6 +129,9 @@ const TerminalInner: FCWithChildren = () => { refresh(); }, [network]); + const successBg = alpha(theme.palette.success.main, theme.palette.mode === 'dark' ? 0.22 : 0.12); + const successColor = theme.palette.mode === 'dark' ? theme.palette.success.light : theme.palette.success.dark; + return ( <Dialog open @@ -90,96 +139,99 @@ const TerminalInner: FCWithChildren = () => { maxWidth="md" fullWidth PaperComponent={Paper} - PaperProps={{ elevation: 0 }} + PaperProps={{ elevation: 0, sx: { borderRadius: 3, overflow: 'hidden' } }} > <NymCard title={ - <Box width="100%" display="flex" justifyContent="space-between"> - <Box display="flex" alignItems="center"> - <TerminalIcon sx={{ mr: 1 }} /> - <Typography mr={4}>Terminal</Typography> - {!isBusy && <RefreshIcon onClick={refresh} cursor="pointer" />} - </Box> - <CloseIcon onClick={handleShowTerminal} cursor="pointer" /> + <Box width="100%" display="flex" justifyContent="space-between" alignItems="center"> + <Stack direction="row" alignItems="center" spacing={1}> + <TerminalIcon sx={{ color: 'primary.main' }} /> + <Typography component="span" fontWeight={600}> + Terminal + </Typography> + {!isBusy && ( + <IconButton size="small" onClick={refresh} aria-label="Refresh state"> + <RefreshIcon fontSize="small" /> + </IconButton> + )} + </Stack> + <IconButton size="small" onClick={handleShowTerminal} aria-label="Close"> + <CloseIcon fontSize="small" /> + </IconButton> </Box> } dataTestid="terminal-page" + noPadding > - <h2>State Viewer</h2> + <Box sx={{ px: 3, pb: 3, pt: 0 }}> + <Typography variant="h6" fontWeight={700} sx={{ mb: 1.5 }}> + State viewer + </Typography> - {error && <Alert color="error">{error}</Alert>} + {error && ( + <Alert severity="error" sx={{ mb: 2 }}> + {String(error)} + </Alert> + )} - {status ? ( - <Alert color="info" icon={<RefreshIcon />} sx={{ mb: 2 }}> - <Box sx={{ fontWeight: 600 }}>{status}</Box> - </Alert> - ) : ( - <Alert color="success" sx={{ mb: 2 }}> - <Box sx={{ fontWeight: 600 }}>Data loading complete</Box> - </Alert> - )} + {status ? ( + <Alert severity="info" icon={<RefreshIcon fontSize="inherit" />} sx={{ mb: 2, alignItems: 'center' }}> + <Typography variant="body2" fontWeight={600}> + {status} + </Typography> + </Alert> + ) : ( + <Alert + severity="success" + variant="outlined" + sx={{ + mb: 2, + bgcolor: successBg, + color: successColor, + borderColor: alpha(theme.palette.success.main, 0.45), + '& .MuiAlert-icon': { color: successColor }, + }} + > + <Typography variant="body2" fontWeight={600}> + Data loading complete + </Typography> + </Alert> + )} - <TerminalSection heading="App Environment"> - <pre>{JSON.stringify(appEnv, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="App environment">{JSON.stringify(appEnv, null, 2)}</TerminalSection> - <TerminalSection heading="Client Details"> - <pre>{JSON.stringify(clientDetails, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Client details">{JSON.stringify(clientDetails, null, 2)}</TerminalSection> - <TerminalSection heading="User Balance"> - <pre>{JSON.stringify(userBalance, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="User balance">{JSON.stringify(userBalance, null, 2)}</TerminalSection> - <TerminalSection - heading={ - <> - <code>useGetBalance</code> Balance - </> - } - > - <pre>{JSON.stringify(userBalance.balance, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Balance (useGetBalance hook)"> + {JSON.stringify(userBalance.balance, null, 2)} + </TerminalSection> - <TerminalSection - heading={ - <> - <code>useGetBalance</code> Vesting Account Info - </> - } - > - <pre>{JSON.stringify(userBalance.vestingAccountInfo, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Vesting account info (useGetBalance hook)"> + {JSON.stringify(userBalance.vestingAccountInfo, null, 2)} + </TerminalSection> - <TerminalSection - heading={ - <> - <code>useGetBalance</code> Current Vest Period - </> - } - > - <pre>{JSON.stringify(userBalance.currentVestingPeriod, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Current vest period (useGetBalance hook)"> + {JSON.stringify(userBalance.currentVestingPeriod, null, 2)} + </TerminalSection> - <TerminalSection heading="Original Vesting"> - <pre>{JSON.stringify(userBalance.originalVesting, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Original vesting"> + {JSON.stringify(userBalance.originalVesting, null, 2)} + </TerminalSection> - <TerminalSection heading="Mixnode Delegations"> - <pre>{JSON.stringify(mixnodeDelegations, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Mixnode delegations">{JSON.stringify(mixnodeDelegations, null, 2)}</TerminalSection> - <TerminalSection heading="Pending Delegation Events"> - <pre>{JSON.stringify(pendingEvents, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Pending delegation events"> + {JSON.stringify(pendingEvents, null, 2)} + </TerminalSection> - <TerminalSection heading="Pending Vesting Delegation Events"> - <pre>{JSON.stringify(pendingVestingEvents, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Pending vesting delegation events"> + {JSON.stringify(pendingVestingEvents, null, 2)} + </TerminalSection> - <TerminalSection heading="Epoch"> - <pre>{JSON.stringify(epoch, null, 2)}</pre> - </TerminalSection> + <TerminalSection heading="Epoch">{JSON.stringify(epoch, null, 2)}</TerminalSection> + </Box> </NymCard> </Dialog> ); @@ -188,7 +240,6 @@ const TerminalInner: FCWithChildren = () => { export const Terminal: FCWithChildren = () => { const { showTerminal } = useContext(AppContext); - // this is a guard component, that only mounts the terminal component when shown if (!showTerminal) { return null; } diff --git a/nym-wallet/src/stories/Introduction.stories.mdx b/nym-wallet/src/stories/Introduction.stories.mdx deleted file mode 100644 index c22001164e..0000000000 --- a/nym-wallet/src/stories/Introduction.stories.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import { Meta } from '@storybook/addon-docs'; - -<Meta title="Introduction" /> - -# Nym Wallet Storybook - -This is the Storybook for the Nym Wallet. diff --git a/nym-wallet/src/stories/Playground.stories.tsx b/nym-wallet/src/stories/Playground.stories.tsx deleted file mode 100644 index 34c684c3d8..0000000000 --- a/nym-wallet/src/stories/Playground.stories.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import * as React from 'react'; -import { ComponentMeta } from '@storybook/react'; -import { Playground } from '@nymproject/react/playground/Playground'; - -export default { - title: 'Playground', - component: Playground, -} as ComponentMeta<typeof Playground>; - -export const AllControls = () => <Playground />; diff --git a/nym-wallet/src/theme/theme.tsx b/nym-wallet/src/theme/theme.tsx index 4683161188..8384c14105 100644 --- a/nym-wallet/src/theme/theme.tsx +++ b/nym-wallet/src/theme/theme.tsx @@ -7,6 +7,9 @@ import { PaletteOptions, ThemeOptions, createTheme, Components } from '@mui/mate * IMPORTANT: do not export this constant, always use the MUI `useTheme` hook to get the correct * colours for dark/light mode. */ +/** Calmer accent for light mode (less neon on pale backgrounds). Dark mode keeps `nymPalette.highlight`. */ +const LIGHT_MODE_PRIMARY_GREEN = 'rgb(18, 149, 96)'; + const nymPalette = { /** emphasises important elements */ highlight: 'rgb(20, 231, 111)', @@ -81,47 +84,50 @@ const darkMode = { const lightMode = { mode: 'light' as const, background: { - main: '#FFFFFF', - paper: '#F4F6F8', + /** Full-window chrome: clearly off-white (no #fff). */ + main: '#D8DEE4', + /** Cards / panels: a step lighter than main for depth. */ + paper: '#E2E7EC', warn: '#F97316', - grey: '#E2E8EC', - greyStroke: '#8DA3B1', - elevated: '#FFFFFF', - subtle: '#F9FAFB', + grey: '#CCD4DB', + /** Dividers: visible but softer than body text. */ + greyStroke: '#B0BAC4', + elevated: '#E8EDF1', + subtle: '#DFE4EA', }, text: { main: '#1C1B1F', - muted: '#79747E', + muted: '#6B6570', warn: '#F97316', contrast: '#FFFFFF', grey: '#696571', - blue: '#60D7EF', - subdued: '#908E95', + blue: '#4DB8D4', + subdued: '#7A7680', }, topNav: { - background: '#FFFFFF', + background: '#D8DEE4', }, nav: { - background: '#F4F6F8', + background: '#DCE2E8', }, hover: { - background: '#E2E8EC', + background: '#D0D8E0', }, modal: { - border: 'transparent', + border: '#B0BAC4', }, chart: { grey: '#79747E', }, // New modern additions gradients: { - primary: 'linear-gradient(45deg, rgba(20, 231, 111, 0.9), rgba(20, 231, 111, 0.7))', - subtle: 'linear-gradient(180deg, rgba(255, 255, 255, 1), rgba(244, 246, 248, 0.8))', + primary: 'linear-gradient(45deg, rgba(18, 149, 96, 0.9), rgba(18, 149, 96, 0.72))', + subtle: 'linear-gradient(180deg, #DFE4EA 0%, #D8DEE4 100%)', }, shadows: { - light: '0 2px 8px rgba(0, 0, 0, 0.06)', - medium: '0 4px 12px rgba(0, 0, 0, 0.08)', - strong: '0 8px 24px rgba(0, 0, 0, 0.12)', + light: '0 1px 3px rgba(15, 23, 42, 0.06)', + medium: '0 2px 8px rgba(15, 23, 42, 0.08)', + strong: '0 5px 16px rgba(15, 23, 42, 0.1)', }, }; @@ -174,13 +180,34 @@ const variantToMUIPalette = (variant: NymVariant): PaletteOptions => ({ divider: variant.background.greyStroke, }); -const createLightModePalette = (): PaletteOptions => ({ - nym: { - ...nymPalette, - ...nymWalletPalette(lightMode), - }, - ...variantToMUIPalette(lightMode), -}); +const createLightModePalette = (): PaletteOptions => { + const primaryMain = LIGHT_MODE_PRIMARY_GREEN; + return { + nym: { + ...nymPalette, + ...nymWalletPalette(lightMode), + highlight: primaryMain, + success: primaryMain, + linkHover: primaryMain, + text: { + ...nymPalette.text, + muted: '#5f6368', + }, + }, + ...variantToMUIPalette(lightMode), + primary: { + main: primaryMain, + light: alpha(primaryMain, 0.78), + dark: '#107a4f', + contrastText: lightMode.text.contrast, + }, + success: { + main: primaryMain, + light: alpha(primaryMain, 0.8), + dark: '#0d6b44', + }, + }; +}; const createDarkModePalette = (): PaletteOptions => ({ nym: { @@ -190,27 +217,29 @@ const createDarkModePalette = (): PaletteOptions => ({ ...variantToMUIPalette(darkMode), }); +const accentForMode = (mode: PaletteMode): string => + mode === 'light' ? LIGHT_MODE_PRIMARY_GREEN : nymPalette.highlight; + // Define component overrides with proper types const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ MuiCssBaseline: { styleOverrides: { - body: { - scrollbarColor: `${ - mode === 'dark' ? darkMode.background.greyStroke : lightMode.background.greyStroke - } transparent`, - '&::-webkit-scrollbar, & *::-webkit-scrollbar': { - width: '8px', - height: '8px', - backgroundColor: 'transparent', - }, - '&::-webkit-scrollbar-thumb, & *::-webkit-scrollbar-thumb': { - borderRadius: 8, - backgroundColor: mode === 'dark' ? darkMode.background.greyStroke : lightMode.background.greyStroke, - minHeight: 24, - }, - '&::-webkit-scrollbar-corner, & *::-webkit-scrollbar-corner': { - backgroundColor: 'transparent', - }, + html: { + WebkitFontSmoothing: 'auto', + }, + body: ({ theme }: { theme: Theme }) => ({ + backgroundColor: theme.palette.background.default, + }), + // Hide scrollbars app-wide while keeping overflow scroll behavior (Firefox / legacy Edge). + '*': { + scrollbarWidth: 'none', + msOverflowStyle: 'none', + }, + // Chromium / WebKit: hide scrollbars on all scrollable elements. + '*::-webkit-scrollbar': { + display: 'none', + width: 0, + height: 0, }, }, }, @@ -236,8 +265,16 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ // Use different approach for overriding MUI styles that have type issues containedPrimary: () => ({ background: mode === 'dark' ? darkMode.gradients.primary : lightMode.gradients.primary, + color: '#FFFFFF', '&:hover': { background: mode === 'dark' ? darkMode.gradients.primary : lightMode.gradients.primary, + color: '#FFFFFF', + }, + '&.Mui-disabled': { + opacity: 1, + backgroundImage: 'none', + backgroundColor: mode === 'dark' ? alpha(nymPalette.highlight, 0.35) : alpha(LIGHT_MODE_PRIMARY_GREEN, 0.35), + color: alpha('#FFFFFF', 0.8), }, }), outlined: { @@ -260,9 +297,9 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ }, MuiCard: { styleOverrides: { - // Use callback format for proper typing - root: () => ({ + root: ({ theme }) => ({ borderRadius: 16, + backgroundColor: theme.palette.background.paper, boxShadow: mode === 'dark' ? darkMode.shadows.light : lightMode.shadows.light, transition: 'transform 0.3s, box-shadow 0.3s', '&:hover': { @@ -317,6 +354,9 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ }, input: { padding: '14px 16px', + '&.MuiInputBase-inputSizeSmall': { + padding: '8.5px 14px', + }, }, }, }, @@ -338,7 +378,7 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ '&.Mui-checked': { transform: 'translateX(28px)', '& + .MuiSwitch-track': { - backgroundColor: nymPalette.highlight, + backgroundColor: accentForMode(mode), opacity: 1, border: 0, }, @@ -493,8 +533,8 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ borderRadius: 8, fontWeight: 500, '&.MuiChip-colorPrimary': { - background: mode === 'dark' ? alpha(nymPalette.highlight, 0.15) : alpha(nymPalette.highlight, 0.12), - color: nymPalette.highlight, + background: mode === 'dark' ? alpha(accentForMode(mode), 0.15) : alpha(accentForMode(mode), 0.12), + color: accentForMode(mode), }, }, label: { @@ -517,7 +557,7 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ fontWeight: 500, transition: 'color 0.2s', '&:hover': { - color: nymPalette.linkHover, + color: accentForMode(mode), }, }, }, @@ -567,7 +607,7 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ color: nymPalette.success, }, '&.Mui-active': { - color: nymPalette.highlight, + color: accentForMode(mode), }, }, }, @@ -581,12 +621,12 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ boxShadow: mode === 'dark' ? darkMode.shadows.medium : lightMode.shadows.medium, mt: 1, '&& .Mui-selected': { - color: nymPalette.highlight, + color: accentForMode(mode), backgroundColor: mode === 'dark' ? alpha(darkMode.background.main, 0.9) : alpha(lightMode.background.main, 0.9), }, '&& .Mui-selected:hover': { - backgroundColor: mode === 'dark' ? alpha(nymPalette.highlight, 0.08) : alpha(nymPalette.highlight, 0.08), + backgroundColor: mode === 'dark' ? alpha(accentForMode(mode), 0.08) : alpha(accentForMode(mode), 0.08), }, '& .MuiMenuItem-root': { borderRadius: 1, @@ -636,9 +676,10 @@ const getComponentOverrides = (mode: PaletteMode): Components<Theme> => ({ }, MuiPaper: { styleOverrides: { - root: { + root: ({ theme }) => ({ backgroundImage: 'none', - }, + backgroundColor: theme.palette.background.paper, + }), // Using function format for styleOverrides to avoid type errors elevation1: () => ({ boxShadow: mode === 'dark' ? darkMode.shadows.light : lightMode.shadows.light, diff --git a/nym-wallet/src/utils/qrCodeReact.tsx b/nym-wallet/src/utils/qrCodeReact.tsx new file mode 100644 index 0000000000..f8c1a02729 --- /dev/null +++ b/nym-wallet/src/utils/qrCodeReact.tsx @@ -0,0 +1,16 @@ +/* eslint-disable global-require, @typescript-eslint/no-require-imports */ +import type { ComponentType } from 'react'; + +export type QrCodeReactProps = { + value: string; + size?: number; + level?: 'L' | 'M' | 'Q' | 'H'; + includeMargin?: boolean; + bgColor?: string; + fgColor?: string; + renderAs?: 'canvas' | 'svg'; + 'data-testid'?: string; +}; + +/** qrcode.react is CJS; its package types do not match bundler default interop. */ +export const QrCodeReact = require('qrcode.react') as ComponentType<QrCodeReactProps>; diff --git a/nym-wallet/src/utils/safeOpenUrl.ts b/nym-wallet/src/utils/safeOpenUrl.ts new file mode 100644 index 0000000000..780f455286 --- /dev/null +++ b/nym-wallet/src/utils/safeOpenUrl.ts @@ -0,0 +1,20 @@ +import { openUrl } from '@tauri-apps/plugin-opener'; + +const ALLOWED_SCHEMES = new Set(['https:', 'http:']); + +/** + * Opens a URL in the system browser via Tauri opener. + * Only http and https are allowed to reduce open-redirect / unexpected scheme risk. + */ +export async function safeOpenUrl(url: string): Promise<void> { + let parsed: URL; + try { + parsed = new URL(url); + } catch { + throw new Error('Invalid URL'); + } + if (!ALLOWED_SCHEMES.has(parsed.protocol)) { + throw new Error(`URL scheme not allowed: ${parsed.protocol}`); + } + await openUrl(url); +} diff --git a/nym-wallet/tsconfig.eslint.json b/nym-wallet/tsconfig.eslint.json index 836be28a14..6908e45f0e 100644 --- a/nym-wallet/tsconfig.eslint.json +++ b/nym-wallet/tsconfig.eslint.json @@ -5,13 +5,11 @@ "noEmit": true }, "include": [ - ".storybook/*.js", "webpack.*.js", "src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx", - "src/**/*.stories.*", ], "exclude": [ "node_modules", diff --git a/nym-wallet/tsconfig.json b/nym-wallet/tsconfig.json index 46f3c3c206..a394536047 100644 --- a/nym-wallet/tsconfig.json +++ b/nym-wallet/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "target": "ES2022", + "lib": ["dom", "dom.iterable", "ES2022"], "allowJs": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, @@ -16,7 +16,8 @@ "sourceMap": true, "baseUrl": ".", "paths": { - "@assets/*": ["../assets/*"] + "@assets/*": ["../assets/*"], + "@tauri-apps/plugin-process": ["../node_modules/@tauri-apps/plugin-process"] } }, diff --git a/nym-wallet/webdriver/.gitignore b/nym-wallet/webdriver/.gitignore deleted file mode 100644 index 34ad668654..0000000000 --- a/nym-wallet/webdriver/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -reports -allure-results -node_modules -.vscode -.idea \ No newline at end of file diff --git a/nym-wallet/webdriver/README.md b/nym-wallet/webdriver/README.md deleted file mode 100644 index acd346ec57..0000000000 --- a/nym-wallet/webdriver/README.md +++ /dev/null @@ -1,86 +0,0 @@ -<!-- -Copyright 2020 - Nym Technologies SA <contact@nymtech.net> -SPDX-License-Identifier: Apache-2.0 ---> - -# Nym Wallet Webdriverio testsuite - -A webdriverio test suite implementation using tauri driver -with a page object model design. This is to provide quick iterative feedback -on the UI of the nym wallet. Currently, tauri-driver is available to run on Windows and Linux machines. - -## Installation prerequisites - -- `Yarn` -- `NodeJS >= v16.8.0` -- `Rust & cargo >= v1.56.1` -- `tauri-driver` -- `That you have an existing mnemonic and you can login to the app` -- `Have the details listed below to provide the user-data.json file` - -## Key Information - -- Please read the instructions on the `nym/tauri-wallet/README.md` in the root of the project on how to build the application -- Please ensure you have the relevant Webdriver kits installed on your machine - - -``` -linux: - sudo apt-get install -y webkit2gtk-driver -``` - -``` -windows: -download msedgedriver.exe from https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ -``` - -please visit [Tauri Studio](https://tauri.studio/en/docs/usage/guides/webdriver/introduction), this will specify the additional drivers you need - -- The path to run the application is set in the `wdio.conf.js` which lives in the root directory -- Before running the suite you need to build the application and check that the application has - built successfully, if so, you will have an executable sitting in the target directory in `tauri-wallet/target/*/nym_wallet` (refer to point 1) -- The suite will not be able to detect elements on screen if you select a release build, however you can run tests against a release target - -## Installation & usage - -- `test excution happens inside /webdriver directory` -- `test data needs to be provided inside the user-data.json` -- `check the wdio.conf.cjs to see the test execution along with the path location of the binary` - -``` -example: -//mnemonic is a base64 enconded value, which is your 24 character passphrase, these values are for illustration purposes - { - "mnemonic" : "dGhpcyBpcyBhIHBhc3NwaHJhc2UK", - "punk_address" : "punk1f3dzkhmunma5ze5q952daxca6371989189", - "receiver_address" : "punk1p0ce82jxxglpmutvhq4mdwgcwf4avm5n1821982", - "amount_to_send" : "1", - "identity_key_to_delegate_mix_node": "value", - "identity_key_to_delegate_gateway" : "value", - "delegate_amount" : "1" - } -``` - -- `yarn test:runall` - the first test run will take some time to spin up be patient -- You can run tests individually by passing through the script situated in the package.json for example `yarn test:newuser` - -Tests are categorised and run by their pages, they follow a sequential flow, if one test case fails before the next execution it may derail the next test. - -//todo improve in near future - -## Test reporting - -Currently the tests use allure reporting, the configuration can be altered in the `wdio.conf.cjs`. At present it takes snapshots of any failing tests, the test output run can be seen in the allure-results directory -Tests ouput: - -- <guid-testuite.xml> -- <guid-attachment.png> - -If any tests fail in their test run it will produce the stack trace error along with the test in question - -## TODO - -_Disclaimer_: Still WIP - -Implement error handling/ beforeTest() - validating json file exists with data for test execution - -Currently this is dev'd against a Linux based OS, not tested against windows yet. diff --git a/nym-wallet/webdriver/babel.config.js b/nym-wallet/webdriver/babel.config.js deleted file mode 100644 index 723cec7c52..0000000000 --- a/nym-wallet/webdriver/babel.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - presets: [ - [ - "@babel/preset-env", - { - targets: { - node: "14", - }, - }, - ], - ], -}; diff --git a/nym-wallet/webdriver/common/constants/text-constants.js b/nym-wallet/webdriver/common/constants/text-constants.js deleted file mode 100644 index 088abf06c7..0000000000 --- a/nym-wallet/webdriver/common/constants/text-constants.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - //receivePage - recievePageInformation: - "You can receive tokens by providing this address to the sender", - receivePageHeaderText: "Receive Nym", - - //sendPage - sendPunk: "Send punk", - - //homePage - homePageErrorMnemonic: "Error parsing bip39 mnemonic", - homePageSignIn: "Sign in", - createOne: "Create one", - walletSuccess: - "Please store your mnemonic in a safe place. You'll need it to access your wallet", - - //bondPage // unbondPage - bondAlreadyNoded: "Looks like you already have a mixnode bonded.", - bondNodeHeaderText: "Bond a node or gateway", - unbondNodeHeaderText: "Unbond a mixnode or gateway", - unbondMixNodeText: "Looks like you already have a mixnode bonded.", - unbondMixNode: "UNBOND", - - //delegatePage // undelegatePage - delegateHeaderText: "Delegate\nDelegate to mixnode", - nodeIdentityValidationText: "identity is a required field", - amountValidationText: "amount is a required field", - undelegateHeaderText: "Undelegate from a mixnode or gateway", - delegationComplete: "Delegation complete", -}; diff --git a/nym-wallet/webdriver/common/data/user-data.json b/nym-wallet/webdriver/common/data/user-data.json deleted file mode 100644 index 4b4b4afa0b..0000000000 --- a/nym-wallet/webdriver/common/data/user-data.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "mnemonic": "value", - "punk_address": "", - "receiver_address": "", - "amount_to_send": "", - "identity_key_to_delegate_mix_node": "", - "identity_key_to_delegate_gateway": "", - "delegate_amount": "" -} diff --git a/nym-wallet/webdriver/common/helpers/helper.js b/nym-wallet/webdriver/common/helpers/helper.js deleted file mode 100644 index 7f8ac61385..0000000000 --- a/nym-wallet/webdriver/common/helpers/helper.js +++ /dev/null @@ -1,43 +0,0 @@ -class Helpers { - //helper to decode mnemonic so plain 24 character passphrase isn't in sight albeit it is presented when ruunning the scripts - //maybe a show passphrase toggle button? - decodeBase = async (input) => { - var m = Buffer.from(input, "base64").toString(); - return m; - }; - - navigateAndClick = async (element) => { - await element.click(); - }; - - scrollIntoView = async (element) => { - await element.scrollIntoView(); - }; - - currentBalance = async (value) => { - return parseFloat(value.split(/\s+/)[0].toString()).toFixed(5); - }; - - //todo need to improve calculation - WIP - calculateFees = async (beforeBalance, transactionFee, amount, isSend) => { - let fee; - - if (isSend) { - //send transaction - fee = transactionFee.split(/\s+/)[0]; - } else { - //delegate transaction - fee = transactionFee.split(/\s+/)[3]; - } - - const currentBalance = beforeBalance.split(/\s+/)[0]; - - const castCurrentBalance = parseFloat(currentBalance).toFixed(5); - const transCost = +parseFloat(amount) + +parseFloat(fee).toFixed(5); - - let sum = parseFloat(castCurrentBalance) - parseFloat(transCost); - return sum.toFixed(5); - }; -} - -module.exports = new Helpers(); diff --git a/nym-wallet/webdriver/package.json b/nym-wallet/webdriver/package.json deleted file mode 100644 index b705dc7912..0000000000 --- a/nym-wallet/webdriver/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "tauri_nym_wallet", - "version": "1.0.0", - "private": false, - "license": "MIT", - "scripts": { - "test:runall": "wdio run wdio.conf.cjs", - "test:sendreceive": "wdio run wdio.conf.cjs --suite sendreceive", - "test:home": "wdio run wdio.conf.cjs --suite home", - "test:bond": "wdio run wdio.conf.cjs --suite bond", - "test:delegate": "wdio run wdio.conf.cjs --suite delegate", - "test:newuser": "wdio run wdio.conf.cjs --suite newuser", - "run:prettier": "prettier --write ." - }, - "dependencies": { - "@types/node": "^16.11.0", - "@wdio/allure-reporter": "^7.16.1", - "@wdio/cli": "^7.9.1", - "@zxing/browser": "^0.0.9" - }, - "devDependencies": { - "@wdio/local-runner": "^7.14.1", - "@wdio/mocha-framework": "^7.14.1", - "@wdio/spec-reporter": "^7.14.1", - "prettier": "^2.8.7" - } -} diff --git a/nym-wallet/webdriver/tests/pages/wallet.bond.js b/nym-wallet/webdriver/tests/pages/wallet.bond.js deleted file mode 100644 index 40a9066c6a..0000000000 --- a/nym-wallet/webdriver/tests/pages/wallet.bond.js +++ /dev/null @@ -1,48 +0,0 @@ -class WalletBond { - get header() { - return $( - "#root > div > div:nth-child(2) > div:nth-child(2) > div > div > div > div.MuiCardHeader-root > div > span.MuiTypography-root.MuiCardHeader-subheader.MuiTypography-subtitle1.MuiTypography-colorTextSecondary.MuiTypography-displayBlock" - ); - } - get identityKey() { - return $("#identityKey"); - } - get sphinxKey() { - return $("#sphinxKey"); - } - get amountToBond() { - return $("#amount"); - } - get hostInput() { - return $("#host"); - } - get versionInput() { - return $("version"); - } - get selectAdvancedOptions() { - return $("[type='checkbox']"); - } - get mixPort() { - return $("#mixPort"); - } - get verlocPort() { - return $("#verlocPort"); - } - get httpApiPort() { - return $("#httpApiPort"); - } - get bondButton() { - return $("[data-testid='bond-button']"); - } - get unBondButton() { - return $("[data-testid='un-bond']"); - } - get unBond() { - return $("[data-testid='bond-noded']"); - } - get unBondWarning() { - return $("div.MuiAlert-message"); - } -} - -module.exports = new WalletBond(); diff --git a/nym-wallet/webdriver/tests/pages/wallet.create.js b/nym-wallet/webdriver/tests/pages/wallet.create.js deleted file mode 100644 index 25f82ec81f..0000000000 --- a/nym-wallet/webdriver/tests/pages/wallet.create.js +++ /dev/null @@ -1,24 +0,0 @@ -class WalletCreate { - get createAccount() { - return $("[href='#']"); - } - get create() { - return $("[data-testid='create-button']"); - } - get accountCreatedSuccessfully() { - return $("[data-testid='mnemonic-warning']"); - } - get walletMnemonicValue() { - return $("[data-testid='mnemonic-phrase']"); - } - get punkAddress() { - return $("[data-testid='wallet-address']"); - } - get backToSignIn() { - return $("[data-testid='sign-in-button']"); - } - get signInButton() { - return $("[type='submit']"); - } -} -module.exports = new WalletCreate(); diff --git a/nym-wallet/webdriver/tests/pages/wallet.delegate.js b/nym-wallet/webdriver/tests/pages/wallet.delegate.js deleted file mode 100644 index 7ef8563a6f..0000000000 --- a/nym-wallet/webdriver/tests/pages/wallet.delegate.js +++ /dev/null @@ -1,60 +0,0 @@ -class WalletDelegate { - get header() { - return $("[data-testid='Delegate']"); - } - get nodeIdentity() { - return $("#identity"); - } - get amountToDelegate() { - return $("#amount"); - } - get identityValidation() { - return $("#identity-helper-text"); - } - get amountToDelegateValidation() { - return $("#amount-helper-text"); - } - get delegateStakeButton() { - return $("[data-testid='delegate-button']"); - } - get mixNodeRadioButton() { - return $("[data-testid='mix-node']"); - } - get gateWayRadioButton() { - return $("[data-testid='gate-way']"); - } - get successfullyDelegate() { - return $("[data-testid='delegate-success']"); - } - get finishButton() { - return $("[data-testid='finish-button']"); - } - get transactionFeeAmount() { - return $("[data-testid='fee-amount']"); - } - get accountBalance() { - return $("[data-testid='account-balance']"); - } - - //Undelegate - get unDelegateHeader() { - return $("[data-testid='Undelegate']"); - } - get unNodeIdentity() { - return $("[name='identity']"); - } - get unDelegateFeeText() { - return $("[data-testid='fee-amount']"); - } - get unDelegateGatewayRadioButton() { - return $("[data-testid='gate-way']"); - } - get unMixNodeRadioButton() { - return $("[data-testid='mix-node']"); - } - get unDelegateButton() { - return $("[data-testid='submit-button']"); - } -} - -module.exports = new WalletDelegate(); diff --git a/nym-wallet/webdriver/tests/pages/wallet.homepage.js b/nym-wallet/webdriver/tests/pages/wallet.homepage.js deleted file mode 100644 index 23012c7c2c..0000000000 --- a/nym-wallet/webdriver/tests/pages/wallet.homepage.js +++ /dev/null @@ -1,42 +0,0 @@ -class WalletHome { - get balanceCheck() { - return $( - "#root > div > div:nth-child(2) > div:nth-child(2) > div > div > div > div.MuiCardHeader-root > div > span" - ); - } - get punkBalance() { - return $(""); - } - get punkAddress() { - return $("[data-testid='wallet-address']"); - } - get accountBalance() { - return $("[data-testid='account-balance']"); - } - get balanceButton() { - return $("[href='/balance']"); - } - get sendButton() { - return $("[href='/send']"); - } - get receiveButton() { - return $("[href='/receive']"); - } - get bondButton() { - return $("[href='/bond']"); - } - get unBondButton() { - return $("[href='/unbond']"); - } - get delegateButton() { - return $("[href='/delegate']"); - } - get unDelegateButton() { - return $("[href='/undelegate']"); - } - get logOutButton() { - return $("[data-testid='log-out']"); - } -} - -module.exports = new WalletHome(); diff --git a/nym-wallet/webdriver/tests/pages/wallet.login.js b/nym-wallet/webdriver/tests/pages/wallet.login.js deleted file mode 100644 index 92cd3d848a..0000000000 --- a/nym-wallet/webdriver/tests/pages/wallet.login.js +++ /dev/null @@ -1,31 +0,0 @@ -class WalletLogin { - get signInLabel() { - return $("[data-testid='sign-in']"); - } - get mnemonic() { - return $("#mnemonic"); - } - get signInButton() { - return $("[type='submit']"); - } - get errorValidation() { - return $("[class='MuiAlert-message']"); - } - get accountBalance() { - return $("[data-test-id='account-balance']"); - } - get accountBalanceText() { - return $("[class='MuiAlert-message']"); - } - get walletAddress() { - return $("[data-testid='wallet-address']"); - } - - //login to the application - enterMnemonic = async (mnemonic) => { - await this.mnemonic.addValue(mnemonic); - await this.signInButton.click(); - await this.accountBalance.isExisting(); - }; -} -module.exports = new WalletLogin(); diff --git a/nym-wallet/webdriver/tests/pages/wallet.receive.js b/nym-wallet/webdriver/tests/pages/wallet.receive.js deleted file mode 100644 index f7c27f1118..0000000000 --- a/nym-wallet/webdriver/tests/pages/wallet.receive.js +++ /dev/null @@ -1,37 +0,0 @@ -class WalletReceive { - get receiveNymHeader() { - return $( - "#root > div > div:nth-child(2) > div:nth-child(2) > div > div > div > div.MuiCardHeader-root > div > span" - ); - } - get receiveNymText() { - return $("[data-testid='receive-nym']"); - } - get walletAddress() { - return $("[data-testid='client-address']"); - } - get copyButton() { - return $("[data-testid='copy-button']"); - } - get qrCode() { - return $("[data-testid='qr-code']"); - } - - WaitForButtonChangeOnCopy = async () => { - await this.copyButton.click(); - - await this.copyButton.waitForDisplayed({ timeout: 1500 }); - - await this.copyButton.waitUntil( - async function () { - return (await this.getText()) === "COPIED"; - }, - { - timeout: 1500, - timeoutMsg: "expected text to be different after 1.5s", - } - ); - }; -} - -module.exports = new WalletReceive(); diff --git a/nym-wallet/webdriver/tests/pages/wallet.send.js b/nym-wallet/webdriver/tests/pages/wallet.send.js deleted file mode 100644 index df336c9cd5..0000000000 --- a/nym-wallet/webdriver/tests/pages/wallet.send.js +++ /dev/null @@ -1,52 +0,0 @@ -class WalletSend { - get fromAddress() { - return $("#from"); - } - get toAddress() { - return $("#to"); - } - get amount() { - return $("#amount"); - } - get nextButton() { - return $("[data-testid='button"); - } - get sendHeader() { - return $("[data-testid='Send punk']"); - } - get accountBalance() { - return $("[data-testid='account-balance']"); - } - get amountReviewAndSend() { - return $("[data-testid='Amount']"); - } - get toAddressReviewAndSend() { - return $("[data-testid='To']"); - } - get fromAddressReviewAndSend() { - return $("[data-testid='From']"); - } - get transferFeeAmount() { - return $("[data-testid='Transfer fee']"); - } - get reviewAndSendBackButton() { - return $("[data-testid='back-button']"); - } - get sendButton() { - return $("[data-testid='button']"); - } - get transactionComplete() { - return $("[data-testid='transaction-complete']"); - } - get transactionCompleteRecipient() { - return $("[data-testid='to-address']"); - } - get transactionCompleteAmount() { - return $("[data-testid='send-amount']"); - } - get finishButton() { - return $("[data-testid='button']"); - } -} - -module.exports = new WalletSend(); diff --git a/nym-wallet/webdriver/tests/pages/wallet.undelegate.js b/nym-wallet/webdriver/tests/pages/wallet.undelegate.js deleted file mode 100644 index 664d02ee46..0000000000 --- a/nym-wallet/webdriver/tests/pages/wallet.undelegate.js +++ /dev/null @@ -1,22 +0,0 @@ -class WallentUndelegate { - get transactionFee() { - return $("[data-testid='fee-amount']"); - } - get mixNodeRadioButton() { - return $("[value='mixnode']"); - } - get gatewayRadionButton() { - return $("[value='gateway']"); - } - get nodeIdentity() { - return $("#mui-55011"); - } - get identityHelper() { - return $("#identity-helper-text"); - } - get delegateButton() { - return $("[data-testid='submit-button']"); - } -} - -module.exports = new WallentUndelegate(); diff --git a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.bond.js b/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.bond.js deleted file mode 100644 index 18b9cf1112..0000000000 --- a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.bond.js +++ /dev/null @@ -1,54 +0,0 @@ -const userData = require("../../../common/data/user-data.json"); -const helper = require("../../../common/helpers/helper"); -const walletLogin = require("../../pages/wallet.login"); -const textConstants = require("../../../common/constants/text-constants"); -const walletHomepage = require("../../pages/wallet.homepage"); -const bondPage = require("../../pages/wallet.bond"); - -describe("bonding and unbonding nodes", () => { - it("should have a node already bonded and validate no input fields are enabled", async () => { - const mnemonic = await helper.decodeBase(userData.mnemonic); - - await walletLogin.enterMnemonic(mnemonic); - - await helper.navigateAndClick(walletHomepage.bondButton); - - await helper.scrollIntoView(bondPage.selectAdvancedOptions); - - await bondPage.selectAdvancedOptions.click(); - - //as bond node is mixed expect all the fields to be disabled - const getText = await bondPage.header.getText(); - const getIdentity = await bondPage.identityKey.isEnabled(); - const getSphinxKey = await bondPage.sphinxKey.isEnabled(); - const amountToBond = await bondPage.amountToBond.isEnabled(); - const hostInput = await bondPage.hostInput.isEnabled(); - const verlocPort = await bondPage.verlocPort.isEnabled(); - const httpApiPort = await bondPage.httpApiPort.isEnabled(); - const mixPort = await bondPage.mixPort.isEnabled(); - - //assert all field are not functional - expect(getText).toEqual(textConstants.bondNodeHeaderText); - expect(getIdentity).toEqual(false); - expect(getSphinxKey).toEqual(false); - expect(amountToBond).toEqual(false); - expect(hostInput).toEqual(false); - expect(verlocPort).toEqual(false); - expect(httpApiPort).toEqual(false); - expect(mixPort).toEqual(false); - }); - - it("unbond mix monde screen should be present with the option to unbond", async () => { - //we do not want to unbond our node, check that elements are selectable - await helper.scrollIntoView(walletHomepage.unBondButton); - await helper.navigateAndClick(walletHomepage.unBondButton); - - const getText = await bondPage.header.getText(); - const unbondText = await bondPage.unBondWarning.getText(); - - await bondPage.unBondButton.isClickable(); - //assert all field are not functional - expect(getText).toEqual(textConstants.unbondNodeHeaderText); - expect(unbondText).toEqual(textConstants.unbondMixNodeText); - }); -}); diff --git a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.delegate.js b/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.delegate.js deleted file mode 100644 index 3143e55685..0000000000 --- a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.delegate.js +++ /dev/null @@ -1,108 +0,0 @@ -const userData = require("../../../common/data/user-data.json"); -const helper = require("../../../common/helpers/helper"); -const walletLogin = require("../../pages/wallet.login"); -const textConstants = require("../../../common/constants/text-constants"); -const walletHomepage = require("../../pages/wallet.homepage"); -const delegatePage = require("../../pages/wallet.delegate"); - -describe("delegate to a mix node or gateway", () => { - it("ensure that fields are enabled for existing user", async () => { - const mnemonic = await helper.decodeBase(userData.mnemonic); - - await walletLogin.enterMnemonic(mnemonic); - - await helper.navigateAndClick(walletHomepage.delegateButton); - - const getText = await delegatePage.header.getText(); - - expect(getText).toEqual(textConstants.delegateHeaderText); - }); - - it("submitting the form without input prompts validation errors", async () => { - await delegatePage.delegateStakeButton.click(); - - const getIdentityValidation = - await delegatePage.identityValidation.getText(); - const getAmountValidation = - await delegatePage.amountToDelegateValidation.getText(); - - expect(getIdentityValidation).toEqual( - textConstants.nodeIdentityValidationText - ); - expect(getAmountValidation).toEqual(textConstants.amountValidationText); - }); - - it("input delegate amount to a mix node then broadcast the transaction then check account balances", async () => { - const balanceText = await delegatePage.accountBalance.getText(); - - const getTransfeeAmount = await delegatePage.transactionFeeAmount.getText(); - - await delegatePage.nodeIdentity.setValue( - userData.identity_key_to_delegate_mix_node - ); - - await delegatePage.amountToDelegate.setValue(userData.delegate_amount); - - //transfer fee + amount delegation - const sumCost = await helper.calculateFees( - balanceText, - getTransfeeAmount, - userData.delegate_amount, - false - ); - - await delegatePage.delegateStakeButton.click(); - - await delegatePage.successfullyDelegate.waitForClickable({ - timeout: 10000, - }); - - const getConfirmationText = - await delegatePage.successfullyDelegate.getText(); - expect(getConfirmationText).toContain(textConstants.delegationComplete); - - const availablePunk = await delegatePage.accountBalance.getText(); - //expect new account balance - the fee calculation above - - await delegatePage.finishButton.click(); - - expect(await helper.currentBalance(availablePunk)).toEqual(sumCost); - }); - - it("input amount to stake to a gateway then broadcast the transaction then check account balances", async () => { - const balanceText = await delegatePage.accountBalance.getText(); - - const getTransfeeAmount = await delegatePage.transactionFeeAmount.getText(); - - await delegatePage.gateWayRadioButton.click(); - - await delegatePage.nodeIdentity.setValue( - userData.identity_key_to_delegate_gateway - ); - - await delegatePage.amountToDelegate.setValue(userData.delegate_amount); - - //transfer fee + amount delegation - - const sumCost = await helper.calculateFees( - balanceText, - getTransfeeAmount, - userData.delegate_amount, - false - ); - - await delegatePage.delegateStakeButton.click(); - - await delegatePage.successfullyDelegate.waitForClickable({ - timeout: 10000, - }); - - const getConfirmationText = - await delegatePage.successfullyDelegate.getText(); - expect(getConfirmationText).toContain(textConstants.delegationComplete); - - const availablePunk = await delegatePage.accountBalance.getText(); - //expect new account balance - the fee calculation above - expect(await helper.currentBalance(availablePunk)).toEqual(sumCost); - }); -}); diff --git a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.home.js b/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.home.js deleted file mode 100644 index 0355cdb78b..0000000000 --- a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.home.js +++ /dev/null @@ -1,45 +0,0 @@ -const userData = require("../../../common/data/user-data.json"); -const helper = require("../../../common/helpers/helper"); -const walletLogin = require("../../pages/wallet.login"); -const homepPage = require("../../pages/wallet.homepage"); -const textConstants = require("../../../common/constants/text-constants"); - -describe("wallet splash screen", () => { - it("should have the sign in header present", async () => { - const signInText = await walletLogin.signInLabel.getText(); - expect(signInText).toEqual(textConstants.homePageSignIn); - }); - - it("submitting the sign in button with no input throws a validation error", async () => { - await walletLogin.signInButton.click(); - - const errorResponseText = await walletLogin.errorValidation.getText(); - expect(errorResponseText).toEqual(textConstants.homePageErrorMnemonic); - }); - - //currently the punk_address is not fully displayed on the wallet UI - //trim the punk address - it("successfully input mnemonic and log in", async () => { - const mnemonic = await helper.decodeBase(userData.mnemonic); - - await walletLogin.enterMnemonic(mnemonic); - - await walletLogin.walletAddress.waitForEnabled({ timeout: 5000 }); - - const getWalletAddress = await walletLogin.walletAddress.getText(); - //currently 35 characters are displayed along with three ... - //current hack we can assume this is the correct wallet - const walletTruncated = userData.punk_address.substring(0, 35); - - expect(walletTruncated + "...").toContain(getWalletAddress); - }); - - it("successfully log out the application", async () => { - await helper.scrollIntoView(homepPage.logOutButton); - - await homepPage.logOutButton.click(); - - await walletLogin.signInLabel.waitForEnabled({ timeout: 1500 }); - expect(await walletLogin.signInLabel.isDisplayed()).toEqual(true); - }); -}); diff --git a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.receive.js b/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.receive.js deleted file mode 100644 index 1983bbfc11..0000000000 --- a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.receive.js +++ /dev/null @@ -1,28 +0,0 @@ -const userData = require("../../../common/data/user-data.json"); -const textConstants = require("../../../common/constants/text-constants"); -const helper = require("../../../common/helpers/helper"); -const walletLogin = require("../../pages/wallet.login"); -const receive = require("../../pages/wallet.receive"); -const walletHomepage = require("../../pages/wallet.homepage"); - -describe("provide the relevant information about a user nym wallet address", () => { - it("should have the receivers address and a qr code present", async () => { - const mnemonic = await helper.decodeBase(userData.mnemonic); - - await walletLogin.enterMnemonic(mnemonic); - - await helper.navigateAndClick(walletHomepage.receiveButton); - - await receive.receiveNymHeader.waitForDisplayed({ timeout: 1500 }); - - await receive.WaitForButtonChangeOnCopy(); - - const textHeader = await receive.receiveNymHeader.getText(); - const getInformationText = await receive.receiveNymText.getText(); - const getPunkAddress = await receive.walletAddress.getText(); - - expect(getPunkAddress).toEqual(userData.punk_address); - expect(getInformationText).toEqual(textConstants.recievePageInformation); - expect(textConstants.receivePageHeaderText).toEqual(textHeader); - }); -}); diff --git a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.send.js b/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.send.js deleted file mode 100644 index 927ca2a86e..0000000000 --- a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.send.js +++ /dev/null @@ -1,55 +0,0 @@ -const userData = require("../../../common/data/user-data.json"); -const helper = require("../../../common/helpers/helper"); -const textConstants = require("../../../common/constants/text-constants"); -const walletLogin = require("../../pages/wallet.login"); -const sendWallet = require("../../pages/wallet.send"); -const walletHomepage = require("../../pages/wallet.homepage"); - -describe("send punk to another a wallet", () => { - it("expect send screen to display the data", async () => { - const mnemonic = await helper.decodeBase(userData.mnemonic); - - await walletLogin.enterMnemonic(mnemonic); - - await helper.navigateAndClick(walletHomepage.sendButton); - - const textHeader = await sendWallet.sendHeader.getText(); - - expect(textHeader).toContain(textConstants.sendPunk); - }); - - it("send funds correctly to another punk address", async () => { - //already logged in due to the previous test - const getCurrentBalance = await walletHomepage.accountBalance.getText(); - - await sendWallet.toAddress.addValue(userData.receiver_address); - - await sendWallet.amount.addValue(userData.amount_to_send); - - await sendWallet.nextButton.waitForEnabled({ timeout: 3000 }); - - await sendWallet.nextButton.click(); - - const transFee = await sendWallet.transferFeeAmount.getText(); - - await sendWallet.sendButton.click(); - - await sendWallet.finishButton.waitForClickable({ timeout: 10000 }); - - let sumCost = await helper.calculateFees( - getCurrentBalance, - transFee, - userData.amount_to_send, - true - ); - - await walletHomepage.accountBalance.isDisplayed(); - - const availablePunk = await walletHomepage.accountBalance.getText(); - - await sendWallet.finishButton.click(); - - //expect new account balance - the fee calculation above - expect(await helper.currentBalance(availablePunk)).toEqual(sumCost); - }); -}); diff --git a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.undelegate.js b/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.undelegate.js deleted file mode 100644 index a544825c3b..0000000000 --- a/nym-wallet/webdriver/tests/specs/existinguser/test.wallet.undelegate.js +++ /dev/null @@ -1,32 +0,0 @@ -const userData = require("../../../common/data/user-data.json"); -const helper = require("../../../common/helpers/helper"); -const walletLogin = require("../../pages/wallet.login"); -const walletHomepage = require("../../pages/wallet.homepage"); -const unDelegatePage = require("../../pages/wallet.delegate"); - -describe("un-delegate a mix node or gateway", () => { - it("ensure that fields are enabled for existing user", async () => { - //we are ensuring that the fields are selectable for undelegation - //not proceeding to undelegate a node or gateway - - const mnemonic = await helper.decodeBase(userData.mnemonic); - - await walletLogin.enterMnemonic(mnemonic); - - await helper.scrollIntoView(walletHomepage.unDelegateButton); - - await helper.navigateAndClick(walletHomepage.unDelegateButton); - - await unDelegatePage.unDelegateButton.waitForClickable({ timeout: 1500 }); - - await unDelegatePage.unDelegateButton.isEnabled(); - - await unDelegatePage.unDelegateGatewayRadioButton.click(); - - await unDelegatePage.unDelegateGatewayRadioButton.isSelected(); - - const mixNodeRadioButton = - await unDelegatePage.unMixNodeRadioButton.isSelected(); - expect(mixNodeRadioButton).toEqual(false); - }); -}); diff --git a/nym-wallet/webdriver/tests/specs/newuser/test.wallet.create.js b/nym-wallet/webdriver/tests/specs/newuser/test.wallet.create.js deleted file mode 100644 index e70cd603ed..0000000000 --- a/nym-wallet/webdriver/tests/specs/newuser/test.wallet.create.js +++ /dev/null @@ -1,39 +0,0 @@ -const walletLogin = require("../../pages/wallet.login"); -const walletSignUp = require("../../pages/wallet.create"); -const textConstants = require("../../../common/constants/text-constants"); - -describe("non existing wallet holder", () => { - //wallet mnemonic gets pushed here - const DATA = []; - it("create a new account and wallet", async () => { - const signInText = await walletLogin.signInLabel.getText(); - expect(signInText).toEqual(textConstants.homePageSignIn); - - await walletSignUp.createAccount.click(); - - //wallet generation takes some time - apply wait - await walletSignUp.create.click(); - - await walletSignUp.accountCreatedSuccessfully.waitForEnabled({ - timeout: 10000, - }); - - const getWalletText = await walletSignUp.punkAddress.getText(); - expect(getWalletText.length).toEqual(43); - - const accountCreated = - await walletSignUp.accountCreatedSuccessfully.getText(); - expect(accountCreated).toEqual(textConstants.walletSuccess); - - const getMnemonic = await walletSignUp.walletMnemonicValue.getText(); - DATA.push(getMnemonic); - }); - - it("navigate back to sign in screen and validate mnemonic works", async () => { - await walletSignUp.backToSignIn.click(); - - await walletLogin.enterMnemonic(DATA[0]); - - await walletLogin.walletAddress.isDisplayed(); - }); -}); diff --git a/nym-wallet/webdriver/wdio.conf.cjs b/nym-wallet/webdriver/wdio.conf.cjs deleted file mode 100644 index c91d7370fc..0000000000 --- a/nym-wallet/webdriver/wdio.conf.cjs +++ /dev/null @@ -1,93 +0,0 @@ -const os = require("os"); -const path = require("path"); -const { spawn, spawnSync } = require("child_process"); -//insert path to binary -const nym_path = "../target/release/nym-wallet"; - -exports.config = { - //run sequentially, as using one default user may cause issues for parallel test runs for now - specs: [ - "./tests/specs/existinguser/test.wallet.home.js", - "./tests/specs/existinguser/test.wallet.send.js", - "./tests/specs/existinguser/test.wallet.receive.js", - "./tests/specs/existinguser/test.wallet.bond.js", - "./tests/specs/existinguser/test.wallet.delegate.js", - "./tests/specs/newuser/test.wallet.create.js", - ], - - //run tests by providing --suite {{login}} - suites: { - home: ["./tests/specs/existinguser/test.wallet.home.js"], - sendreceive: [ - "./tests/specs/existinguser/test.wallet.send.js", - "./tests/specs/existinguser/test.wallet.receive.js", - ], - bond: ["./tests/specs/existinguser/test.wallet.bond.js"], - delegate: [ - "./tests/specs/existinguser/test.wallet.delegate.js", - "./tests/specs/existinguser/test.wallet.undelegate.js", - ], - newuser: ["./tests/specs/newuser/test.wallet.create.js"], - }, - maxInstances: 1, - capabilities: [ - { - maxInstances: 1, - "tauri:options": { - application: nym_path, - }, - }, - ], - // =================== - // Test Configurations - // =================== - // Define all options that are relevant for the WebdriverIO instance here - // Level of logging verbosity: trace | debug | info | warn | error | silent - bail: 0, - framework: "mocha", - reporters: ["spec"], - mochaOpts: { - ui: "bdd", - timeout: 60000, - }, - logLevel: "silent", - - // =================== - // Test Reporters - // =================== - reporters: [ - [ - "allure", - { - outputDir: "allure-results", - disableWebdriverStepsReporting: true, - disableWebdriverScreenshotsReporting: true, - }, - ], - ], - - // this is documentented in the readme - you will need to build the project first - // ensure the rust project is built since we expect this binary to exist for the webdriver sessions - //onPrepare: () => spawnSync("cargo", ["build", "--release"]), - - // ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests - beforeSession: () => - (tauriDriver = spawn( - path.resolve(os.homedir(), ".cargo", "bin", "tauri-driver"), - [], - { stdio: [null, process.stdout, process.stderr] } - )), - - afterTest: function ( - test, - context, - { error, result, duration, passed, retries } - ) { - if (error) { - browser.takeScreenshot(); - } - }, - - // clean up the `tauri-driver` process we spawned at the start of the session - afterSession: () => tauriDriver.kill(), -}; diff --git a/nym-wallet/webdriver/yarn.lock b/nym-wallet/webdriver/yarn.lock deleted file mode 100644 index 962fe62b55..0000000000 --- a/nym-wallet/webdriver/yarn.lock +++ /dev/null @@ -1,2709 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.12.13": - version "7.15.8" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503" - integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg== - dependencies: - "@babel/highlight" "^7.14.5" - -"@babel/helper-validator-identifier@^7.14.5": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - -"@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@jest/types@^27.2.5": - version "27.2.5" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.2.5.tgz#420765c052605e75686982d24b061b4cbba22132" - integrity sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - -"@sindresorhus/is@^4.0.0": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" - integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== - -"@szmarczak/http-timer@^4.0.5": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" - integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== - dependencies: - defer-to-connect "^2.0.0" - -"@types/aria-query@^4.2.1": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== - -"@types/cacheable-request@^6.0.1": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" - integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "*" - "@types/node" "*" - "@types/responselike" "*" - -"@types/diff@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.1.tgz#9c9b9a331d4e41ccccff553f5d7ef964c6cf4042" - integrity sha512-XIpxU6Qdvp1ZE6Kr3yrkv1qgUab0fyf4mHYvW8N3Bx3PCsbN6or1q9/q72cv5jIFWolaGH08U9XyYoLLIykyKQ== - -"@types/easy-table@^0.0.33": - version "0.0.33" - resolved "https://registry.yarnpkg.com/@types/easy-table/-/easy-table-0.0.33.tgz#b1f7ec29014ec24906b4f28d8368e2e99b399313" - integrity sha512-/vvqcJPmZUfQwCgemL0/34G7bIQnCuvgls379ygRlcC1FqNqk3n+VZ15dAO51yl6JNDoWd8vsk+kT8zfZ1VZSw== - -"@types/ejs@^3.0.5": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.0.tgz#ab8109208106b5e764e5a6c92b2ba1c625b73020" - integrity sha512-DCg+Ka+uDQ31lJ/UtEXVlaeV3d6t81gifaVWKJy4MYVVgvJttyX/viREy+If7fz+tK/gVxTGMtyrFPnm4gjrVA== - -"@types/fs-extra@^9.0.4": - version "9.0.13" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" - integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== - dependencies: - "@types/node" "*" - -"@types/http-cache-semantics@*": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" - integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== - -"@types/inquirer@^8.1.2": - version "8.1.3" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.1.3.tgz#dfda4c97cdbe304e4dceb378a80f79448ea5c8fe" - integrity sha512-AayK4ZL5ssPzR1OtnOLGAwpT0Dda3Xi/h1G0l1oJDNrowp7T1423q4Zb8/emr7tzRlCy4ssEri0LWVexAqHyKQ== - dependencies: - "@types/through" "*" - rxjs "^7.2.0" - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== - -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/json-buffer@~3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" - integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== - -"@types/keyv@*": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" - integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== - dependencies: - "@types/node" "*" - -"@types/lodash.flattendeep@^4.4.6": - version "4.4.6" - resolved "https://registry.yarnpkg.com/@types/lodash.flattendeep/-/lodash.flattendeep-4.4.6.tgz#2686d9161ae6c3d56d6745fa118308d88562ae53" - integrity sha512-uLm2MaRVlqJSGsMK0RZpP5T3KqReq+9WbYDHCUhBhp98v56hMG/Yht52bsoTSui9xz2mUvQ9NfG3LrNGDL92Ng== - dependencies: - "@types/lodash" "*" - -"@types/lodash.pickby@^4.6.6": - version "4.6.6" - resolved "https://registry.yarnpkg.com/@types/lodash.pickby/-/lodash.pickby-4.6.6.tgz#3dc39c2b38432f7a0c5e5627b0d5c0e3878b4f35" - integrity sha512-NFa13XxlMd9eFi0UFZFWIztpMpXhozbijrx3Yb1viYZphT7jyopIFVoIRF4eYMjruWNEG1rnyrRmg/8ej9T8Iw== - dependencies: - "@types/lodash" "*" - -"@types/lodash.union@^4.6.6": - version "4.6.6" - resolved "https://registry.yarnpkg.com/@types/lodash.union/-/lodash.union-4.6.6.tgz#2f77f2088326ed147819e9e384182b99aae8d4b0" - integrity sha512-Wu0ZEVNcyCz8eAn6TlUbYWZoGbH9E+iOHxAZbwUoCEXdUiy6qpcz5o44mMXViM4vlPLLCPlkAubEP1gokoSZaw== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.14.176" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0" - integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ== - -"@types/mocha@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.0.0.tgz#3205bcd15ada9bc681ac20bef64e9e6df88fd297" - integrity sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA== - -"@types/node@*": - version "18.0.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" - integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA== - -"@types/node@^16.11.0", "@types/node@^16.11.1": - version "16.11.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.1.tgz#2e50a649a50fc403433a14f829eface1a3443e97" - integrity sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA== - -"@types/node@^17.0.4": - version "17.0.23" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" - integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== - -"@types/object-inspect@^1.8.0": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@types/object-inspect/-/object-inspect-1.8.1.tgz#7c08197ad05cc0e513f529b1f3919cc99f720e1f" - integrity sha512-0JTdf3CGV0oWzE6Wa40Ayv2e2GhpP3pEJMcrlM74vBSJPuuNkVwfDnl0SZxyFCXETcB4oKA/MpTVfuYSMOelBg== - -"@types/recursive-readdir@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@types/recursive-readdir/-/recursive-readdir-2.2.0.tgz#b39cd5474fd58ea727fe434d5c68b7a20ba9121c" - integrity sha512-HGk753KRu2N4mWduovY4BLjYq4jTOL29gV2OfGdGxHcPSWGFkC5RRIdk+VTs5XmYd7MVAD+JwKrcb5+5Y7FOCg== - dependencies: - "@types/node" "*" - -"@types/responselike@*", "@types/responselike@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== - dependencies: - "@types/node" "*" - -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/stream-buffers@^3.0.3": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/stream-buffers/-/stream-buffers-3.0.4.tgz#bf128182da7bc62722ca0ddf5458a9c65f76e648" - integrity sha512-qU/K1tb2yUdhXkLIATzsIPwbtX6BpZk0l3dPW6xqWyhfzzM1ECaQ/8faEnu3CNraLiQ9LHyQQPBGp7N9Fbs25w== - dependencies: - "@types/node" "*" - -"@types/supports-color@^8.1.0": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-8.1.1.tgz#1b44b1b096479273adf7f93c75fc4ecc40a61ee4" - integrity sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw== - -"@types/through@*": - version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" - integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== - dependencies: - "@types/node" "*" - -"@types/tmp@^0.2.0": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.1.tgz#83ecf4ec22a8c218c71db25f316619fe5b986011" - integrity sha512-7cTXwKP/HLOPVgjg+YhBdQ7bMiobGMuoBmrGmqwIWJv8elC6t1DfVc/mn4fD9UE1IjhwmhaQ5pGVXkmXbH0rhg== - -"@types/which@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.2.tgz#9c246fc0c93ded311c8512df2891fb41f6227fdf" - integrity sha512-8oDqyLC7eD4HM307boe2QWKyuzdzWBj56xI/imSl2cpL+U3tCMaTAkMJ4ee5JBZ/FsOJlvRGeIShiZDAl1qERA== - -"@types/yargs-parser@*": - version "20.2.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" - integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== - -"@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== - dependencies: - "@types/yargs-parser" "*" - -"@types/yauzl@^2.9.1": - version "2.9.2" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" - integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== - dependencies: - "@types/node" "*" - -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - -"@wdio/allure-reporter@^7.16.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@wdio/allure-reporter/-/allure-reporter-7.19.1.tgz#185e5daab0c7bd4a9390d24e589db0e01249fb69" - integrity sha512-nHz8dimpg1+Gtoysd3n9sBUf3XCwUvHgSPzEBXFAAL9h41tNAf3XJXoeaMAY1m+XqD0TThjkcTMomPdh6CmvCQ== - dependencies: - "@types/node" "^17.0.4" - "@wdio/reporter" "7.19.1" - "@wdio/types" "7.19.1" - allure-js-commons "^1.3.2" - csv-stringify "^6.0.4" - strip-ansi "^6.0.0" - -"@wdio/cli@^7.9.1": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/cli/-/cli-7.16.0.tgz#8246bc269d9a74e7f492524963c277df353d0e6b" - integrity sha512-B0O0hjGxzmCTiGNE7DL7PqZPtIkQjTxgh1ZP9BZir4gfJwZlYh3BaLXtqPhEm6gzEI8w2YUhHbXwPq+8iCVkrA== - dependencies: - "@types/ejs" "^3.0.5" - "@types/fs-extra" "^9.0.4" - "@types/inquirer" "^8.1.2" - "@types/lodash.flattendeep" "^4.4.6" - "@types/lodash.pickby" "^4.6.6" - "@types/lodash.union" "^4.6.6" - "@types/node" "^16.11.1" - "@types/recursive-readdir" "^2.2.0" - "@wdio/config" "7.16.0" - "@wdio/logger" "7.16.0" - "@wdio/types" "7.16.0" - "@wdio/utils" "7.16.0" - async-exit-hook "^2.0.1" - chalk "^4.0.0" - chokidar "^3.0.0" - cli-spinners "^2.1.0" - ejs "^3.0.1" - fs-extra "^10.0.0" - inquirer "8.1.5" - lodash.flattendeep "^4.4.0" - lodash.pickby "^4.6.0" - lodash.union "^4.6.0" - mkdirp "^1.0.4" - recursive-readdir "^2.2.2" - webdriverio "7.16.0" - yargs "^17.0.0" - yarn-install "^1.0.0" - -"@wdio/config@7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/config/-/config-7.16.0.tgz#1d68197f0059909187586a39efb98f6d11219055" - integrity sha512-BrHJvdiA1hPmVu69/X7TBz0kxDri584MW1meBHOyPcJAwcavdmK4AcaS1v5hcZ1zOZPl+cTuF5QN2q3Y56z7hw== - dependencies: - "@wdio/logger" "7.16.0" - "@wdio/types" "7.16.0" - deepmerge "^4.0.0" - glob "^7.1.2" - -"@wdio/local-runner@^7.14.1": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/local-runner/-/local-runner-7.16.0.tgz#8c07ab9f369a0f0a411d41825c4a90fa4357c9af" - integrity sha512-PY82qgCeB62OQURmrAp61eGhuQzQ4zXehC8+NsHHiKrPgUcT4ha54LiZY5Pt3pZvGoAXOSF1RNwZlGcz5/8ZfA== - dependencies: - "@types/stream-buffers" "^3.0.3" - "@wdio/logger" "7.16.0" - "@wdio/repl" "7.16.0" - "@wdio/runner" "7.16.0" - "@wdio/types" "7.16.0" - async-exit-hook "^2.0.1" - split2 "^4.0.0" - stream-buffers "^3.0.2" - -"@wdio/logger@7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-7.16.0.tgz#40f116ebffc23c638b8e421e350f110a058523e9" - integrity sha512-/6lOGb2Iow5eSsy7RJOl1kCwsP4eMlG+/QKro5zUJsuyNJSQXf2ejhpkzyKWLgQbHu83WX6cM1014AZuLkzoQg== - dependencies: - chalk "^4.0.0" - loglevel "^1.6.0" - loglevel-plugin-prefix "^0.8.4" - strip-ansi "^6.0.0" - -"@wdio/mocha-framework@^7.14.1": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/mocha-framework/-/mocha-framework-7.16.0.tgz#b5ec31669b4a63ac6d1796368ccfc712ac593f9d" - integrity sha512-tqxaW0br/KMwqytFtT3FDI3ESS66cI52JOp09zp8fk1FQZp+4kFs9XblGU55jqarQVxPYqf2h8+ezm+t2p3Cxg== - dependencies: - "@types/mocha" "^9.0.0" - "@wdio/logger" "7.16.0" - "@wdio/types" "7.16.0" - "@wdio/utils" "7.16.0" - expect-webdriverio "^3.0.0" - mocha "^9.0.0" - -"@wdio/protocols@7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/protocols/-/protocols-7.16.0.tgz#76887cad1e88fd74769f0cb2ce43e681593e192b" - integrity sha512-95z2sJZ3W+J1z8gPAlvxRH45Ct/ZMl8+m8cDj1zrnrJ9VcWW7seW6ksyBUSegyQ0S6JmeR6qqIKhewDpjq0NSw== - -"@wdio/repl@7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/repl/-/repl-7.16.0.tgz#dfe0992519710c9bc4cdb60b4db1f1bbaf4cfa1a" - integrity sha512-FI4E5MLI81sb125TvLV4DHsxd+2XjXxzxTaaCSx+/GBNuR8JjC2/GhCJV9v7jb7mxBSX18ab2+t8dkKlAqWACA== - dependencies: - "@wdio/utils" "7.16.0" - -"@wdio/reporter@7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/reporter/-/reporter-7.16.0.tgz#92a6dd1ab134dc65db4739dfde3db8adafe53895" - integrity sha512-u4rBmj0QQSxJ/LKlpIk1N2/wwjO3VmKPNJ6DXsfgkgbxjldzDOms4NXvM/l2O/Ej74p42K9OHEJXf//s1zqS8Q== - dependencies: - "@types/diff" "^5.0.0" - "@types/node" "^16.11.1" - "@types/object-inspect" "^1.8.0" - "@types/supports-color" "^8.1.0" - "@types/tmp" "^0.2.0" - "@wdio/types" "7.16.0" - diff "^5.0.0" - fs-extra "^10.0.0" - object-inspect "^1.10.3" - supports-color "8.1.1" - -"@wdio/reporter@7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@wdio/reporter/-/reporter-7.19.1.tgz#5c996247c0ac33d215a6b596ef1a4a5e9e1a7c69" - integrity sha512-sWmBBV4dPCZkGk9Qq0m35T/vHGen0N10nH4osQcVP3IZJqpo2eLIH4w+X6EUbjZ2GdgOA2bLMMzb1bl9JqnGPg== - dependencies: - "@types/diff" "^5.0.0" - "@types/node" "^17.0.4" - "@types/object-inspect" "^1.8.0" - "@types/supports-color" "^8.1.0" - "@types/tmp" "^0.2.0" - "@wdio/types" "7.19.1" - diff "^5.0.0" - fs-extra "^10.0.0" - object-inspect "^1.10.3" - supports-color "8.1.1" - -"@wdio/runner@7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/runner/-/runner-7.16.0.tgz#536ca9f8009c1d3c08dece48242d6b596f4f35be" - integrity sha512-G6AElg5yhexe7ioB65kQGebKv0rRUMR2rt8w+igq4f3CUBqGKVDiYZIJqNAhzndjJ/A/BDzZRyRUcdMPzezPYg== - dependencies: - "@wdio/config" "7.16.0" - "@wdio/logger" "7.16.0" - "@wdio/types" "7.16.0" - "@wdio/utils" "7.16.0" - deepmerge "^4.0.0" - gaze "^1.1.2" - webdriver "7.16.0" - webdriverio "7.16.0" - -"@wdio/spec-reporter@^7.14.1": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/spec-reporter/-/spec-reporter-7.16.0.tgz#0af1261b8204e2dc0e07aaf17ce8bb590e27cc82" - integrity sha512-8dj+OZ7S89mT27yXPzsuj1nCWnZtwGUw0E7h4Kl9lKRrDGsz8qddIpxNrP9NTDBgNmvlW4+hP6uWDm4rjxqIjw== - dependencies: - "@types/easy-table" "^0.0.33" - "@wdio/reporter" "7.16.0" - "@wdio/types" "7.16.0" - chalk "^4.0.0" - easy-table "^1.1.1" - pretty-ms "^7.0.0" - -"@wdio/types@7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/types/-/types-7.16.0.tgz#4c86f1d38e044c2e7bd1e55bbbc2075ff8d444dc" - integrity sha512-qXl+icSQQ9tKn3lgpjuxMFtHj34r74/JlaHxGTgjzP8ghDDJCn1xH94mUL5C0MBBeNezxvfWYOb+DzxiQoqblw== - dependencies: - "@types/node" "^16.11.1" - got "^11.8.1" - -"@wdio/types@7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@wdio/types/-/types-7.19.1.tgz#9e7d67cb7b25876f26e20c7c22a8ce78fb294685" - integrity sha512-mOodKlmvYxpj8P5BhjggEGpXuiRSlsyn2ClG8QqJ3lfXgOtOVEzFNfv/Ai7TkHr+lHDQNXLjllCjSqoCHhwlqg== - dependencies: - "@types/node" "^17.0.4" - got "^11.8.1" - -"@wdio/utils@7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@wdio/utils/-/utils-7.16.0.tgz#ffab38d12ce27277c4dafd44c002f417fc85b7b1" - integrity sha512-eSZWkSmVTfQU+lVOK8KUn5AXagpzC2BseGWDsqZME47BwoKtov3H7q6xfalDG89YkZMgO8K04RAlEDDRNodUMA== - dependencies: - "@wdio/logger" "7.16.0" - "@wdio/types" "7.16.0" - p-iteration "^1.1.8" - -"@zxing/browser@^0.0.9": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@zxing/browser/-/browser-0.0.9.tgz#1227bec71a7c9f9f4a697c2f029dc82ba0cbb56e" - integrity sha512-yqYz/FHQXbUnfSK+oeDXUa4ezC/qdXkVpqEFAnxM7LqIWvNWEQyUpdaTr0X6MGtIcP0Smakj5D8J7/l276plBw== - optionalDependencies: - "@zxing/text-encoding" "^0.9.0" - -"@zxing/text-encoding@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" - integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== - -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -allure-js-commons@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/allure-js-commons/-/allure-js-commons-1.3.2.tgz#e1cf0466e36695bb3ced1228f6570eac6c2e9eda" - integrity sha512-FTmoqP36ZjHFT4iLdYamyCFhyj1jqD6BIdiZ5pBlyafDJrFRV76XIXNxwRqbHpSw40o1vHzYi4vGpmREnhnHVw== - dependencies: - file-type "^7.7.1" - fs-extra "^6.0.1" - js2xmlparser "^3.0.0" - mime "^2.3.1" - object-assign "^4.1.1" - uuid "^3.0.0" - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -archiver-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" - integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== - dependencies: - glob "^7.1.4" - graceful-fs "^4.2.0" - lazystream "^1.0.0" - lodash.defaults "^4.2.0" - lodash.difference "^4.5.0" - lodash.flatten "^4.4.0" - lodash.isplainobject "^4.0.6" - lodash.union "^4.6.0" - normalize-path "^3.0.0" - readable-stream "^2.0.0" - -archiver@^5.0.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.0.tgz#dd3e097624481741df626267564f7dd8640a45ba" - integrity sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg== - dependencies: - archiver-utils "^2.1.0" - async "^3.2.0" - buffer-crc32 "^0.2.1" - readable-stream "^3.6.0" - readdir-glob "^1.0.0" - tar-stream "^2.2.0" - zip-stream "^4.1.0" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -aria-query@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" - integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== - -async-exit-hook@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz#8bd8b024b0ec9b1c01cccb9af9db29bd717dfaf3" - integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== - -async@^3.2.0, async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bl@^4.0.3, bl@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.3, braces@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" - integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== - dependencies: - fill-range "^7.1.1" - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer@^5.2.1, buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -cac@^3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/cac/-/cac-3.0.4.tgz#6d24ceec372efe5c9b798808bc7f49b47242a4ef" - integrity sha1-bSTO7Dcu/lybeYgIvH9JtHJCpO8= - dependencies: - camelcase-keys "^3.0.0" - chalk "^1.1.3" - indent-string "^3.0.0" - minimist "^1.2.0" - read-pkg-up "^1.0.1" - suffix "^0.1.0" - text-table "^0.2.0" - -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - -cacheable-request@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" - integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^6.0.1" - responselike "^2.0.0" - -camelcase-keys@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-3.0.0.tgz#fc0c6c360363f7377e3793b9a16bccf1070c1ca4" - integrity sha1-/AxsNgNj9zd+N5O5oWvM8QcMHKQ= - dependencies: - camelcase "^3.0.0" - map-obj "^1.0.0" - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= - -camelcase@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== - -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -chokidar@3.5.2, chokidar@^3.0.0: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chrome-launcher@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.14.1.tgz#e885e706f26cc7a07d348e730fe12f15e43b0881" - integrity sha512-iQ4s61NkIyaozsE2VKg1Vu3YGdD3JGw+fBBrt3FYJi7uflO9TvlTLW4MUq0fq3EKGhzB/QHPd5AsLb14+9++JQ== - dependencies: - "@types/node" "*" - escape-string-regexp "^4.0.0" - is-wsl "^2.2.0" - lighthouse-logger "^1.0.0" - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^2.1.0, cli-spinners@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" - integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== - dependencies: - mimic-response "^1.0.0" - -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -compress-brotli@^1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db" - integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ== - dependencies: - "@types/json-buffer" "~3.0.0" - json-buffer "~3.0.1" - -compress-commons@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d" - integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ== - dependencies: - buffer-crc32 "^0.2.13" - crc32-stream "^4.0.2" - normalize-path "^3.0.0" - readable-stream "^3.6.0" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -crc-32@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" - integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== - dependencies: - exit-on-epipe "~1.0.1" - printj "~1.1.0" - -crc32-stream@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007" - integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w== - dependencies: - crc-32 "^1.2.0" - readable-stream "^3.4.0" - -cross-spawn@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" - integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -css-shorthand-properties@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz#1c808e63553c283f289f2dd56fcee8f3337bd935" - integrity sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A== - -css-value@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/css-value/-/css-value-0.0.1.tgz#5efd6c2eea5ea1fd6b6ac57ec0427b18452424ea" - integrity sha1-Xv1sLupeof1rasV+wEJ7GEUkJOo= - -csv-stringify@^6.0.4: - version "6.0.5" - resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-6.0.5.tgz#3474a4fe784249eb5c91d455f616e1f70961cdc0" - integrity sha512-7xpV3uweJCFF/Ssn56l3xsR/k2r3UqszwjEhej9qEn2cCPzyK1WyHCgoUVzBA792x8HbwonNX7CU9XM2K5s5yw== - -debug@4, debug@4.3.2, debug@^4.1.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -debug@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -decompress-response@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" - integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - dependencies: - mimic-response "^3.1.0" - -deepmerge@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - -defer-to-connect@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" - integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== - -devtools-protocol@0.0.901419: - version "0.0.901419" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.901419.tgz#79b5459c48fe7e1c5563c02bd72f8fec3e0cebcd" - integrity sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ== - -devtools-protocol@^0.0.928170: - version "0.0.928170" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.928170.tgz#4a21610efd01b5744d02bd0fc3fef87f825d0277" - integrity sha512-zLPUM55N6OvUzIeCz8ceD48Hm310c4nC4vqrCfAtRlhv7zoSDl3BAeuCmFLHhgA60QF38vMb2YIGAq+BP6QMMA== - -devtools@7.16.0: - version "7.16.0" - resolved "https://registry.yarnpkg.com/devtools/-/devtools-7.16.0.tgz#c78ddeb446358201286537bfe45c285114b3881e" - integrity sha512-W4r2Y5KhXi+w/qQrONxRsBM2hJBVZXv7qm2eoRRCto8vmR+TkJzS/TrD9hOn2XEM/EjcUGHzGEhwOf3NqyPKEQ== - dependencies: - "@types/node" "^16.11.1" - "@wdio/config" "7.16.0" - "@wdio/logger" "7.16.0" - "@wdio/protocols" "7.16.0" - "@wdio/types" "7.16.0" - "@wdio/utils" "7.16.0" - chrome-launcher "^0.14.0" - edge-paths "^2.1.0" - puppeteer-core "^10.1.0" - query-selector-shadow-dom "^1.0.0" - ua-parser-js "^0.7.21" - uuid "^8.0.0" - -diff-sequences@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723" - integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ== - -diff@5.0.0, diff@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - -easy-table@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/easy-table/-/easy-table-1.2.0.tgz#ba9225d7138fee307bfd4f0b5bc3c04bdc7c54eb" - integrity sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww== - dependencies: - ansi-regex "^5.0.1" - optionalDependencies: - wcwidth "^1.0.1" - -edge-paths@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/edge-paths/-/edge-paths-2.2.1.tgz#d2d91513225c06514aeac9843bfce546abbf4391" - integrity sha512-AI5fC7dfDmCdKo3m5y7PkYE8m6bMqR6pvVpgtrZkkhcJXFLelUgkjrhk3kXXx8Kbw2cRaTT4LkOR7hqf39KJdw== - dependencies: - "@types/which" "^1.3.2" - which "^2.0.2" - -ejs@^3.0.1: - version "3.1.10" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" - integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== - dependencies: - jake "^10.8.5" - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -error-ex@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -exit-on-epipe@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" - integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== - -expect-webdriverio@^3.0.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/expect-webdriverio/-/expect-webdriverio-3.1.4.tgz#78d379e23fff1ce1a45ded8d2168734df0ededa3" - integrity sha512-65FTS3bmxcIp0cq6fLb/72TrCQXBCpwPLC7SwMWdpPlLq461mXcK1BPKJJjnIC587aXSKD+3E4hvnlCtwDmXfg== - dependencies: - expect "^27.0.2" - jest-matcher-utils "^27.0.2" - -expect@^27.0.2: - version "27.3.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.3.1.tgz#d0f170b1f5c8a2009bab0beffd4bb94f043e38e7" - integrity sha512-MrNXV2sL9iDRebWPGOGFdPQRl2eDQNu/uhxIMShjjx74T6kC6jFIkmQ6OqXDtevjGUkyB2IT56RzDBqXf/QPCg== - dependencies: - "@jest/types" "^27.2.5" - ansi-styles "^5.0.0" - jest-get-type "^27.3.1" - jest-matcher-utils "^27.3.1" - jest-message-util "^27.3.1" - jest-regex-util "^27.0.6" - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extract-zip@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -fast-deep-equal@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" - integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-type@^7.7.1: - version "7.7.1" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.7.1.tgz#91c2f5edb8ce70688b9b68a90d931bbb6cb21f65" - integrity sha512-bTrKkzzZI6wH+NXhyD3SOXtb2zXTw2SbwI2RxUlRcXVsnN7jNL5hJzVQLYv7FOQhxFkK4XWdAflEaWFpaLLWpQ== - -filelist@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.3.tgz#448607750376484932f67ef1b9ff07386b036c83" - integrity sha512-LwjCsruLWQULGYKy7TX0OPtrL9kLpojOFKc5VCTxdFTV7w5zbsgqVKfnkKG7Qgjtq50gKfO56hJv88OfcGb70Q== - dependencies: - minimatch "^5.0.1" - -fill-range@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" - integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== - dependencies: - to-regex-range "^5.0.1" - -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-extra@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" - integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" - integrity sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -gaze@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" - integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== - dependencies: - globule "^1.0.0" - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-port@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@7.1.7, glob@~7.1.1: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globule@^1.0.0: - version "1.3.3" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.3.tgz#811919eeac1ab7344e905f2e3be80a13447973c2" - integrity sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg== - dependencies: - glob "~7.1.1" - lodash "~4.17.10" - minimatch "~3.0.2" - -got@^11.0.2, got@^11.8.1: - version "11.8.5" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" - integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.2" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== - -grapheme-splitter@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -http-cache-semantics@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== - -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - -https-proxy-agent@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== - dependencies: - agent-base "6" - debug "4" - -iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inquirer@8.1.5: - version "8.1.5" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.1.5.tgz#2dc5159203c826d654915b5fe6990fd17f54a150" - integrity sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.21" - mute-stream "0.0.8" - ora "^5.4.1" - run-async "^2.4.0" - rxjs "^7.2.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-core-module@^2.2.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" - integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== - dependencies: - has "^1.0.3" - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -jake@^10.8.5: - version "10.8.5" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" - integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== - dependencies: - async "^3.2.3" - chalk "^4.0.2" - filelist "^1.0.1" - minimatch "^3.0.4" - -jest-diff@^27.3.1: - version "27.3.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.3.1.tgz#d2775fea15411f5f5aeda2a5e02c2f36440f6d55" - integrity sha512-PCeuAH4AWUo2O5+ksW4pL9v5xJAcIKPUPfIhZBcG1RKv/0+dvaWTQK1Nrau8d67dp65fOqbeMdoil+6PedyEPQ== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.0.6" - jest-get-type "^27.3.1" - pretty-format "^27.3.1" - -jest-get-type@^27.3.1: - version "27.3.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.3.1.tgz#a8a2b0a12b50169773099eee60a0e6dd11423eff" - integrity sha512-+Ilqi8hgHSAdhlQ3s12CAVNd8H96ZkQBfYoXmArzZnOfAtVAJEiPDBirjByEblvG/4LPJmkL+nBqPO3A1YJAEg== - -jest-matcher-utils@^27.0.2, jest-matcher-utils@^27.3.1: - version "27.3.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.3.1.tgz#257ad61e54a6d4044e080d85dbdc4a08811e9c1c" - integrity sha512-hX8N7zXS4k+8bC1Aj0OWpGb7D3gIXxYvPNK1inP5xvE4ztbz3rc4AkI6jGVaerepBnfWB17FL5lWFJT3s7qo8w== - dependencies: - chalk "^4.0.0" - jest-diff "^27.3.1" - jest-get-type "^27.3.1" - pretty-format "^27.3.1" - -jest-message-util@^27.3.1: - version "27.3.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.3.1.tgz#f7c25688ad3410ab10bcb862bcfe3152345c6436" - integrity sha512-bh3JEmxsTZ/9rTm0jQrPElbY2+y48Rw2t47uMfByNyUVR+OfPh4anuyKsGqsNkXk/TI4JbLRZx+7p7Hdt6q1yg== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.2.5" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.4" - pretty-format "^27.3.1" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-regex-util@^27.0.6: - version "27.0.6" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.0.6.tgz#02e112082935ae949ce5d13b2675db3d8c87d9c5" - integrity sha512-SUhPzBsGa1IKm8hx2F4NfTGGp+r7BXJ4CulsZ1k2kI+mGLG+lxGrs76veN2LF/aUdGosJBzKgXmNCw+BzFqBDQ== - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -js2xmlparser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-3.0.0.tgz#3fb60eaa089c5440f9319f51760ccd07e2499733" - integrity sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM= - dependencies: - xmlcreate "^1.0.1" - -json-buffer@3.0.1, json-buffer@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -keyv@^4.0.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2" - integrity sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw== - dependencies: - compress-brotli "^1.3.8" - json-buffer "3.0.1" - -ky@^0.28.5: - version "0.28.6" - resolved "https://registry.yarnpkg.com/ky/-/ky-0.28.6.tgz#3dfe7a33be3e3d3c99a19d1703ec7a5dcb662ec5" - integrity sha512-EjxET5qSsaLUj1BSFtxPjEtRgF5JOhdroPwMNJFH/VvzruWQFBmh6W7GtqjBR56UZw4dBFTKLvx9nDxxnFXc7w== - -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= - dependencies: - readable-stream "^2.0.5" - -lighthouse-logger@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.3.0.tgz#ba6303e739307c4eee18f08249524e7dafd510db" - integrity sha512-BbqAKApLb9ywUli+0a+PcV04SyJ/N1q/8qgCNe6U97KbPCS1BTksEuHFLYdvc8DltuhfxIUBqDZsC0bBGtl3lA== - dependencies: - debug "^2.6.9" - marky "^1.2.2" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - -lodash.defaults@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= - -lodash.difference@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" - integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw= - -lodash.flatten@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= - -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= - -lodash.isobject@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" - integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0= - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - -lodash.merge@^4.6.1: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.pickby@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" - integrity sha1-feoh2MGNdwOifHBMFdO4SmfjOv8= - -lodash.union@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" - integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= - -lodash.zip@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" - integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= - -lodash@^4.17.21, lodash@~4.17.10: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@4.1.0, log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -loglevel-plugin-prefix@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz#2fe0e05f1a820317d98d8c123e634c1bd84ff644" - integrity sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g== - -loglevel@^1.6.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" - integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -map-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - -marky@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.2.tgz#4456765b4de307a13d263a69b0c79bf226e68323" - integrity sha512-k1dB2HNeaNyORco8ulVEhctyEGkKHb2YWAhDsxeFlW2nROIirsctBYzKwwS3Vza+sKTS1zO4Z+n9/+9WbGLIxQ== - -micromatch@^4.0.4: - version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" - integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== - dependencies: - braces "^3.0.3" - picomatch "^2.3.1" - -mime@^2.3.1: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -mimic-response@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" - integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - -minimatch@3.0.4, minimatch@~3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^3.0.4: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== - dependencies: - brace-expansion "^2.0.1" - -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -mkdirp@^0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -mocha@^9.0.0: - version "9.1.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb" - integrity sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw== - dependencies: - "@ungap/promise-all-settled" "1.1.2" - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.2" - debug "4.3.2" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.1.7" - growl "1.10.5" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "3.0.4" - ms "2.1.3" - nanoid "3.1.25" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - which "2.0.2" - workerpool "6.1.5" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -nanoid@3.1.25: - version "3.1.25" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" - integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== - -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-inspect@^1.10.3: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-cancelable@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" - integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== - -p-iteration@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/p-iteration/-/p-iteration-1.1.8.tgz#14df726d55af368beba81bcc92a26bb1b48e714a" - integrity sha512-IMFBSDIYcPNnW7uWYGrBqmvTiq7W0uB0fJn6shQZs7dlF3OvrHOre+JT9ikSZ7gZS3vWqclVgoQSvToJrns7uQ== - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= - dependencies: - error-ex "^1.2.0" - -parse-ms@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" - integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-parse@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - -picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pkg-dir@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -prettier@^2.8.7: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - -pretty-format@^27.3.1: - version "27.3.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.3.1.tgz#7e9486365ccdd4a502061fa761d3ab9ca1b78df5" - integrity sha512-DR/c+pvFc52nLimLROYjnXPtolawm+uWDxr4FjuLDLUn+ktWnSN851KoHwHzzqq6rfCOjkzN8FLgDrSub6UDuA== - dependencies: - "@jest/types" "^27.2.5" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" - -pretty-ms@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" - integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== - dependencies: - parse-ms "^2.1.0" - -printj@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" - integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -progress@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31" - integrity sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg== - -proxy-from-env@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -puppeteer-core@^10.1.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-10.4.0.tgz#4e2da57c52339b8c07cd9362922020eb3c3c95bf" - integrity sha512-KU8zyb7AIOqNjLCN3wkrFXxh+EVaG+zrs2P03ATNjc3iwSxHsu5/EvZiREpQ/IJiT9xfQbDVgKcsvRuzLCxglQ== - dependencies: - debug "4.3.1" - devtools-protocol "0.0.901419" - extract-zip "2.0.1" - https-proxy-agent "5.0.0" - node-fetch "2.6.1" - pkg-dir "4.2.0" - progress "2.0.1" - proxy-from-env "1.1.0" - rimraf "3.0.2" - tar-fs "2.0.0" - unbzip2-stream "1.3.3" - ws "7.4.6" - -query-selector-shadow-dom@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.0.tgz#8fa7459a4620f094457640e74e953a9dbe61a38e" - integrity sha512-bK0/0cCI+R8ZmOF1QjT7HupDUYCxbf/9TJgAmSXQxZpftXmTAeil9DRoCnTDkWbvOyZzhcMBwKpptWcdkGFIMg== - -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -readable-stream@^2.0.0, readable-stream@^2.0.5: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdir-glob@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.1.tgz#f0e10bb7bf7bfa7e0add8baffdc54c3f7dbee6c4" - integrity sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA== - dependencies: - minimatch "^3.0.4" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -recursive-readdir@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" - integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== - dependencies: - minimatch "3.0.4" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -resolve-alpn@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" - integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== - -resolve@^1.10.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -responselike@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" - integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== - dependencies: - lowercase-keys "^2.0.0" - -resq@^1.9.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/resq/-/resq-1.10.1.tgz#c05d1b3808016cceec4d485ceb375acb49565f53" - integrity sha512-zhp1iyUH02MLciv3bIM2bNtTFx/fqRsK4Jk73jcPqp00d/sMTTjOtjdTMAcgjrQKGx5DvQ/HSpeqaMW0atGRJA== - dependencies: - fast-deep-equal "^2.0.1" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -rgb2hex@0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/rgb2hex/-/rgb2hex-0.2.5.tgz#f82230cd3ab1364fa73c99be3a691ed688f8dbdc" - integrity sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw== - -rimraf@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -rxjs@^7.2.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.4.0.tgz#a12a44d7eebf016f5ff2441b87f28c9a51cebc68" - integrity sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w== - dependencies: - tslib "~2.1.0" - -safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -"semver@2 || 3 || 4 || 5": - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -serialize-error@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" - integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== - dependencies: - type-fest "^0.20.2" - -serialize-javascript@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== - dependencies: - randombytes "^2.1.0" - -signal-exit@^3.0.2: - version "3.0.5" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" - integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.10" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" - integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== - -split2@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/split2/-/split2-4.0.0.tgz#c76cb53ad55040ddff3a9c3b73fc88a4690f047c" - integrity sha512-gjmavJzvQCAZzaEHWoJBOwqIUAiEvUOlguQ6uO0+0LTS1tlLa2YetTLWCrm049ouvLOa1l6SOGm3XaiRiCg9SQ== - -stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== - dependencies: - escape-string-regexp "^2.0.0" - -stream-buffers@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-3.0.2.tgz#5249005a8d5c2d00b3a32e6e0a6ea209dc4f3521" - integrity sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ== - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - -strip-json-comments@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -suffix@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/suffix/-/suffix-0.1.1.tgz#cc58231646a0ef1102f79478ef3a9248fd9c842f" - integrity sha1-zFgjFkag7xEC95R47zqSSP2chC8= - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -tar-fs@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" - integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== - dependencies: - chownr "^1.1.1" - mkdirp "^0.5.1" - pump "^3.0.0" - tar-stream "^2.0.0" - -tar-stream@^2.0.0, tar-stream@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -through@^2.3.6, through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -tslib@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -ua-parser-js@^0.7.21: - version "0.7.33" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" - integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== - -unbzip2-stream@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" - integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -uuid@^3.0.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.0.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= - dependencies: - defaults "^1.0.3" - -webdriver@7.16.0: - version "7.16.0" - resolved "https://registry.yarnpkg.com/webdriver/-/webdriver-7.16.0.tgz#55a6ad0827e3a12d6ff804d98f68d81656600a0f" - integrity sha512-MWGlfg9X9Lnz/8uFyEiiwk3PbG+/IpPoOmrfgHgM1i0KfiUiraKAS8H4gt4kQ3J33TbnotG5+mc6xGUBJ0IDdQ== - dependencies: - "@types/node" "^16.11.1" - "@wdio/config" "7.16.0" - "@wdio/logger" "7.16.0" - "@wdio/protocols" "7.16.0" - "@wdio/types" "7.16.0" - "@wdio/utils" "7.16.0" - got "^11.0.2" - ky "^0.28.5" - lodash.merge "^4.6.1" - -webdriverio@7.16.0: - version "7.16.0" - resolved "https://registry.yarnpkg.com/webdriverio/-/webdriverio-7.16.0.tgz#2ea69873beded175c2c7bd3d2a7575a2420db3e9" - integrity sha512-zCRhHyr+VOk0BXSlvBJfqjqKPj6xFTI90Z0AyOaVxI5MytI9nVyySZsKyfK/r4gIcP2BAJwaxPPhaOySFmRm1Q== - dependencies: - "@types/aria-query" "^4.2.1" - "@types/node" "^16.11.1" - "@wdio/config" "7.16.0" - "@wdio/logger" "7.16.0" - "@wdio/protocols" "7.16.0" - "@wdio/repl" "7.16.0" - "@wdio/types" "7.16.0" - "@wdio/utils" "7.16.0" - archiver "^5.0.0" - aria-query "^5.0.0" - atob "^2.1.2" - css-shorthand-properties "^1.1.1" - css-value "^0.0.1" - devtools "7.16.0" - devtools-protocol "^0.0.928170" - fs-extra "^10.0.0" - get-port "^5.1.1" - grapheme-splitter "^1.0.2" - lodash.clonedeep "^4.5.0" - lodash.isobject "^3.0.2" - lodash.isplainobject "^4.0.6" - lodash.zip "^4.2.0" - minimatch "^3.0.4" - puppeteer-core "^10.1.0" - query-selector-shadow-dom "^1.0.0" - resq "^1.9.1" - rgb2hex "0.2.5" - serialize-error "^8.0.0" - webdriver "7.16.0" - -which@2.0.2, which@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -workerpool@6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" - integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -ws@7.4.6: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== - -xmlcreate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-1.0.2.tgz#fa6bf762a60a413fb3dd8f4b03c5b269238d308f" - integrity sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8= - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^17.0.0: - version "17.2.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.2.1.tgz#e2c95b9796a0e1f7f3bf4427863b42e0418191ea" - integrity sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yarn-install@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/yarn-install/-/yarn-install-1.0.0.tgz#57f45050b82efd57182b3973c54aa05cb5d25230" - integrity sha1-V/RQULgu/VcYKzlzxUqgXLXSUjA= - dependencies: - cac "^3.0.3" - chalk "^1.1.3" - cross-spawn "^4.0.2" - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zip-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" - integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A== - dependencies: - archiver-utils "^2.1.0" - compress-commons "^4.1.0" - readable-stream "^3.6.0" diff --git a/nym-wallet/webpack.common.js b/nym-wallet/webpack.common.js index 3d842709c3..071987f15a 100644 --- a/nym-wallet/webpack.common.js +++ b/nym-wallet/webpack.common.js @@ -2,6 +2,15 @@ const path = require('path'); const { mergeWithRules } = require('webpack-merge'); const { webpackCommon } = require('@nymproject/webpack'); +const resolveFromWallet = (request) => require.resolve(request, { paths: [__dirname] }); + +const muiSystemDir = path.dirname( + require.resolve('@mui/system/package.json', { paths: [__dirname, path.resolve(__dirname, '..')] }), +); +const muiStyledEngineV5 = path.dirname( + require.resolve('@mui/styled-engine/package.json', { paths: [muiSystemDir] }), +); + const entry = { auth: path.resolve(__dirname, 'src/auth.tsx'), // JS bundle for sign up/sign in main: path.resolve(__dirname, 'src/main.tsx'), // JS bundle for main app @@ -23,6 +32,18 @@ module.exports = mergeWithRules({ ]), { entry, + resolve: { + // Yarn workspaces hoist deps to ../node_modules; resolve Tauri packages from there too. + modules: [path.resolve(__dirname, 'node_modules'), path.resolve(__dirname, '../node_modules')], + alias: { + '@mui/styled-engine': muiStyledEngineV5, + react$: resolveFromWallet('react'), + 'react-dom$': resolveFromWallet('react-dom'), + 'react-dom/client': resolveFromWallet('react-dom/client'), + 'react/jsx-runtime': resolveFromWallet('react/jsx-runtime'), + 'react/jsx-dev-runtime': resolveFromWallet('react/jsx-dev-runtime'), + }, + }, output: { clean: true, path: path.resolve(__dirname, 'dist'), diff --git a/yarn.lock b/yarn.lock index 52463b02af..4bb722a07b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@adobe/css-tools@^4.0.1": +"@adobe/css-tools@^4.4.0": version "4.4.4" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.4.tgz#2856c55443d3d461693f32d2b96fb6ea92e1ffa9" integrity sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg== @@ -43,11 +43,25 @@ js-tokens "^4.0.0" picocolors "^1.1.1" +"@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== + dependencies: + "@babel/helper-validator-identifier" "^7.28.5" + js-tokens "^4.0.0" + picocolors "^1.1.1" + "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.27.2", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== +"@babel/compat-data@^7.28.6": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" + integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== + "@babel/core@7.12.9": version "7.12.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" @@ -91,6 +105,27 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/core@^7.23.9", "@babel/core@^7.27.4": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" + integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helpers" "^7.28.6" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/remapping" "^2.3.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.28.5", "@babel/generator@^7.7.2": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" @@ -102,6 +137,17 @@ "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" +"@babel/generator@^7.27.5", "@babel/generator@^7.29.0": + version "7.29.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== + dependencies: + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3": version "7.27.3" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5" @@ -120,6 +166,17 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== + dependencies: + "@babel/compat-data" "^7.28.6" + "@babel/helper-validator-option" "^7.27.1" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.3", "@babel/helper-create-class-features-plugin@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46" @@ -188,6 +245,14 @@ "@babel/traverse" "^7.27.1" "@babel/types" "^7.27.1" +"@babel/helper-module-imports@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== + dependencies: + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" + "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" @@ -197,6 +262,15 @@ "@babel/helper-validator-identifier" "^7.27.1" "@babel/traverse" "^7.28.3" +"@babel/helper-module-transforms@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" + integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== + dependencies: + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" + "@babel/helper-optimise-call-expression@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" @@ -280,6 +354,14 @@ "@babel/template" "^7.27.2" "@babel/types" "^7.28.4" +"@babel/helpers@^7.28.6": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.2.tgz#9cfbccb02b8e229892c0b07038052cc1a8709c49" + integrity sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw== + dependencies: + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" @@ -287,6 +369,13 @@ dependencies: "@babel/types" "^7.28.5" +"@babel/parser@^7.23.9", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" + integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== + dependencies: + "@babel/types" "^7.29.0" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz#fbde57974707bbfa0376d34d425ff4fa6c732421" @@ -1163,11 +1252,16 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10", "@babel/runtime@^7.27.6", "@babel/runtime@^7.28.4", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.9", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10", "@babel/runtime@^7.27.6", "@babel/runtime@^7.28.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.7": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== +"@babel/runtime@^7.3.1", "@babel/runtime@^7.8.3": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e" + integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g== + "@babel/runtime@~7.5.4": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" @@ -1184,6 +1278,15 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" +"@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" + "@babel/traverse@^7.1.6", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5", "@babel/traverse@^7.7.2": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" @@ -1197,6 +1300,19 @@ "@babel/types" "^7.28.5" debug "^4.3.1" +"@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" + debug "^4.3.1" + "@babel/types@^7.0.0", "@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.2.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" @@ -1205,6 +1321,14 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" +"@babel/types@^7.28.6", "@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + "@base2/pretty-print-object@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" @@ -1727,14 +1851,14 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== -"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0": +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.4.0": +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2", "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": version "4.12.2" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== @@ -1760,6 +1884,21 @@ dependencies: "@types/json-schema" "^7.0.15" +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + "@eslint/eslintrc@^3.3.1": version "3.3.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz#26393a0806501b5e2b6a43aa588a4d8df67880ac" @@ -1775,6 +1914,11 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" + integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== + "@eslint/js@9.26.0": version "9.26.0" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.26.0.tgz#1e13126b67a3db15111d2dcc61f69a2acff70bd5" @@ -1963,11 +2107,25 @@ "@humanfs/core" "^0.19.1" "@humanwhocodes/retry" "^0.4.0" +"@humanwhocodes/config-array@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748" + integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw== + dependencies: + "@humanwhocodes/object-schema" "^2.0.3" + debug "^4.3.1" + minimatch "^3.0.5" + "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== +"@humanwhocodes/object-schema@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + "@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": version "0.4.3" resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" @@ -2200,6 +2358,18 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@jest/console@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.3.0.tgz#42ccc3f995d400a8fe35b8850cfe10a8d4804cdf" + integrity sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww== + dependencies: + "@jest/types" "30.3.0" + "@types/node" "*" + chalk "^4.1.2" + jest-message-util "30.3.0" + jest-util "30.3.0" + slash "^3.0.0" + "@jest/console@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" @@ -2212,6 +2382,39 @@ jest-util "^27.5.1" slash "^3.0.0" +"@jest/core@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.3.0.tgz#d06bb8456f35350f6494fd2405bcec4abb97b994" + integrity sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw== + dependencies: + "@jest/console" "30.3.0" + "@jest/pattern" "30.0.1" + "@jest/reporters" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" + "@types/node" "*" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + ci-info "^4.2.0" + exit-x "^0.2.2" + graceful-fs "^4.2.11" + jest-changed-files "30.3.0" + jest-config "30.3.0" + jest-haste-map "30.3.0" + jest-message-util "30.3.0" + jest-regex-util "30.0.1" + jest-resolve "30.3.0" + jest-resolve-dependencies "30.3.0" + jest-runner "30.3.0" + jest-runtime "30.3.0" + jest-snapshot "30.3.0" + jest-util "30.3.0" + jest-validate "30.3.0" + jest-watcher "30.3.0" + pretty-format "30.3.0" + slash "^3.0.0" + "@jest/core@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" @@ -2246,10 +2449,20 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/diff-sequences@30.0.1": - version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz#0ededeae4d071f5c8ffe3678d15f3a1be09156be" - integrity sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw== +"@jest/diff-sequences@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz#25b0818d3d83f00b9c7b04e069b8810f9014b143" + integrity sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA== + +"@jest/environment@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.3.0.tgz#b0657c2944b6ef3352f7b25903cc3a23e6ab70f6" + integrity sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw== + dependencies: + "@jest/fake-timers" "30.3.0" + "@jest/types" "30.3.0" + "@types/node" "*" + jest-mock "30.3.0" "@jest/environment@^27.5.1": version "27.5.1" @@ -2261,13 +2474,40 @@ "@types/node" "*" jest-mock "^27.5.1" -"@jest/expect-utils@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.2.0.tgz#4f95413d4748454fdb17404bf1141827d15e6011" - integrity sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA== +"@jest/expect-utils@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.3.0.tgz#c45b2da9802ffed33bf43b3e019ddb95e5ad95e8" + integrity sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA== dependencies: "@jest/get-type" "30.1.0" +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.3.0.tgz#08ee7f5b610167b0068743246c0b568f4c40c773" + integrity sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg== + dependencies: + expect "30.3.0" + jest-snapshot "30.3.0" + +"@jest/fake-timers@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.3.0.tgz#2b2868130c1d28233a79566874c42cae1c5a70bc" + integrity sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ== + dependencies: + "@jest/types" "30.3.0" + "@sinonjs/fake-timers" "^15.0.0" + "@types/node" "*" + jest-message-util "30.3.0" + jest-mock "30.3.0" + jest-util "30.3.0" + "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -2285,6 +2525,16 @@ resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.1.0.tgz#4fcb4dc2ebcf0811be1c04fd1cb79c2dba431cbc" integrity sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA== +"@jest/globals@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.3.0.tgz#40f4c90e5602629ecda1ca773a8fb21575bb64ea" + integrity sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA== + dependencies: + "@jest/environment" "30.3.0" + "@jest/expect" "30.3.0" + "@jest/types" "30.3.0" + jest-mock "30.3.0" + "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" @@ -2302,6 +2552,35 @@ "@types/node" "*" jest-regex-util "30.0.1" +"@jest/reporters@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.3.0.tgz#0c1065f6c892665e5a051df22b19df4466ed816b" + integrity sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" + "@jridgewell/trace-mapping" "^0.3.25" + "@types/node" "*" + chalk "^4.1.2" + collect-v8-coverage "^1.0.2" + exit-x "^0.2.2" + glob "^10.5.0" + graceful-fs "^4.2.11" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^5.0.0" + istanbul-reports "^3.1.3" + jest-message-util "30.3.0" + jest-util "30.3.0" + jest-worker "30.3.0" + slash "^3.0.0" + string-length "^4.0.2" + v8-to-istanbul "^9.0.1" + "@jest/reporters@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" @@ -2347,6 +2626,25 @@ dependencies: "@sinclair/typebox" "^0.27.8" +"@jest/snapshot-utils@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz#ca003c91a3e1e4e4956dee716a2aaf04b6707f31" + integrity sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g== + dependencies: + "@jest/types" "30.3.0" + chalk "^4.1.2" + graceful-fs "^4.2.11" + natural-compare "^1.4.0" + +"@jest/source-map@30.0.1": + version "30.0.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-30.0.1.tgz#305ebec50468f13e658b3d5c26f85107a5620aaa" + integrity sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + callsites "^3.1.0" + graceful-fs "^4.2.11" + "@jest/source-map@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" @@ -2356,6 +2654,16 @@ graceful-fs "^4.2.9" source-map "^0.6.0" +"@jest/test-result@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.3.0.tgz#cd8882d683d467fcffb98c09501a65687a76aae9" + integrity sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ== + dependencies: + "@jest/console" "30.3.0" + "@jest/types" "30.3.0" + "@types/istanbul-lib-coverage" "^2.0.6" + collect-v8-coverage "^1.0.2" + "@jest/test-result@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" @@ -2366,6 +2674,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-sequencer@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz#27002b2093f4e0d9e0e1ebb0bc274a242fdadc14" + integrity sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA== + dependencies: + "@jest/test-result" "30.3.0" + graceful-fs "^4.2.11" + jest-haste-map "30.3.0" + slash "^3.0.0" + "@jest/test-sequencer@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" @@ -2376,6 +2694,26 @@ jest-haste-map "^27.5.1" jest-runtime "^27.5.1" +"@jest/transform@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.3.0.tgz#9e6f78ffa205449bf956e269fd707c160f47ce2f" + integrity sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A== + dependencies: + "@babel/core" "^7.27.4" + "@jest/types" "30.3.0" + "@jridgewell/trace-mapping" "^0.3.25" + babel-plugin-istanbul "^7.0.1" + chalk "^4.1.2" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.11" + jest-haste-map "30.3.0" + jest-regex-util "30.0.1" + jest-util "30.3.0" + pirates "^4.0.7" + slash "^3.0.0" + write-file-atomic "^5.0.1" + "@jest/transform@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" @@ -2418,10 +2756,10 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.2.0.tgz#1c678a7924b8f59eafd4c77d56b6d0ba976d62b8" - integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== +"@jest/types@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.3.0.tgz#cada800d323cb74945c24ac74615fdb312a6c85f" + integrity sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw== dependencies: "@jest/pattern" "30.0.1" "@jest/schemas" "30.0.5" @@ -2499,7 +2837,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": version "0.3.31" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== @@ -2873,7 +3211,7 @@ csstype "^3.1.3" prop-types "^15.8.1" -"@mui/styles@^5.2.2": +"@mui/styles@^5.18.0": version "5.18.0" resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.18.0.tgz#e70330282e2c64880cc5f36f3fd9fa94257177d5" integrity sha512-GDgFUfl2MMN8iGrYTuhJ0hHRoja2kVOlXnHb5d3BBQCHFxOBaAPDKQxaNkoAwRf/vBhhwXWDsJA2XlkNHUPBgA== @@ -3211,7 +3549,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -3530,6 +3868,11 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@pkgr/core@^0.2.9": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" + integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== + "@pmmmwh/react-refresh-webpack-plugin@^0.5.3", "@pmmmwh/react-refresh-webpack-plugin@^0.5.4": version "0.5.17" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.17.tgz#8c2f34ca8651df74895422046e11ce5a120e7930" @@ -4849,6 +5192,20 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^15.0.0": + version "15.3.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz#afecc36681e26aab9e0fe809fd9ad578096a3058" + integrity sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw== + dependencies: + "@sinonjs/commons" "^3.0.1" + "@sinonjs/fake-timers@^8.0.1": version "8.1.0" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" @@ -6029,110 +6386,122 @@ resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz#1dff176df9cc8f93c78c5e46bcea11079b397578" integrity sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA== -"@tauri-apps/api@^2.4.0", "@tauri-apps/api@^2.6.0", "@tauri-apps/api@^2.8.0": +"@tauri-apps/api@^2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.10.1.tgz#57c1bae6114ec33d977eb2b50dfefc25fa84fc93" + integrity sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw== + +"@tauri-apps/api@^2.8.0": version "2.9.0" resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.9.0.tgz#047fcbfec05a719b0cec997eee244cee453fb2fc" integrity sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw== -"@tauri-apps/cli-darwin-arm64@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.9.1.tgz#60bbf0a34098c1feb865d7483433c6e4a0208142" - integrity sha512-sdwhtsE/6njD0AjgfYEj1JyxZH4SBmCJSXpRm6Ph5fQeuZD6MyjzjdVOrrtFguyREVQ7xn0Ujkwvbo01ULthNg== +"@tauri-apps/cli-darwin-arm64@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.1.tgz#7abb013926613555559cce1583ab6521e07b997c" + integrity sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ== -"@tauri-apps/cli-darwin-x64@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.9.1.tgz#e0bdb2e29201dd0b46bffa61cc7dccb5f0c6ae29" - integrity sha512-c86g+67wTdI4TUCD7CaSd/13+oYuLQxVST4ZNJ5C+6i1kdnU3Us1L68N9MvbDLDQGJc9eo0pvuK6sCWkee+BzA== +"@tauri-apps/cli-darwin-x64@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.1.tgz#cd1abd24782d488d0ee26f15015016b83a5b4bde" + integrity sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw== -"@tauri-apps/cli-linux-arm-gnueabihf@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.9.1.tgz#15ad573c32b8941d10c2ebd2ecfe399ea85ed023" - integrity sha512-IrB3gFQmueQKJjjisOcMktW/Gh6gxgqYO419doA3YZ7yIV5rbE8ZW52Q3I4AO+SlFEyVYer5kpi066p0JBlLGw== +"@tauri-apps/cli-linux-arm-gnueabihf@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.1.tgz#9afdc71d24d44941d76fdc353f4401e82e9ccac5" + integrity sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w== -"@tauri-apps/cli-linux-arm64-gnu@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.9.1.tgz#f7a5dd6f46c5a2b52f20a61308cd61dd04694435" - integrity sha512-Ke7TyXvu6HbWSkmVkFbbH19D3cLsd117YtXP/u9NIvSpYwKeFtnbpirrIUfPm44Q+PZFZ2Hvg8X9qoUiAK0zKw== +"@tauri-apps/cli-linux-arm64-gnu@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.1.tgz#3a0a1e31eda8f01b69db09179094e0dc7ff5bb60" + integrity sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA== -"@tauri-apps/cli-linux-arm64-musl@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.9.1.tgz#011caad0d6222f5c18221d7b413e1b4750eef5f4" - integrity sha512-sGvy75sv55oeMulR5ArwPD28DsDQxqTzLhXCrpU9/nbFg/JImmI7k994YE9fr3V0qE3Cjk5gjLldRNv7I9sjwQ== +"@tauri-apps/cli-linux-arm64-musl@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.1.tgz#6fc7e74e4a6053dd64b81dfdaf9dbabb6df31971" + integrity sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg== -"@tauri-apps/cli-linux-riscv64-gnu@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.9.1.tgz#0328624bf798d653c49bc0455d1cc111e631846b" - integrity sha512-tEKbJydV3BdIxpAx8aGHW6VDg1xW4LlQuRD/QeFZdZNTreHJpMbJEcdvAcI+Hg6vgQpVpaoEldR9W4F6dYSLqQ== +"@tauri-apps/cli-linux-riscv64-gnu@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.1.tgz#9a5d3bb8efb17ef65146d0f319c6cf212e2a724a" + integrity sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw== -"@tauri-apps/cli-linux-x64-gnu@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.9.1.tgz#25e0f95615ddede953af4d29ad1c0d1f7c160c3f" - integrity sha512-mg5msXHagtHpyCVWgI01M26JeSrgE/otWyGdYcuTwyRYZYEJRTbcNt7hscOkdNlPBe7isScW7PVKbxmAjJJl4g== +"@tauri-apps/cli-linux-x64-gnu@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.1.tgz#cf54e3754d2a54894323840aef1f9789888e0d57" + integrity sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw== -"@tauri-apps/cli-linux-x64-musl@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.9.1.tgz#ccb819101a225947f42ebb4f1f6902514aa902e3" - integrity sha512-lFZEXkpDreUe3zKilvnMsrnKP9gwQudaEjDnOz/GMzbzNceIuPfFZz0cR/ky1Aoq4eSvZonPKHhROq4owz4fzg== +"@tauri-apps/cli-linux-x64-musl@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.1.tgz#dc4bd17e812b448b3ccec0684f336f19fb47b069" + integrity sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ== -"@tauri-apps/cli-win32-arm64-msvc@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.9.1.tgz#c0be0555d2b3686d4b3ea702039d41ed6017e4eb" - integrity sha512-ejc5RAp/Lm1Aj0EQHaT+Wdt5PHfdgQV5hIDV00MV6HNbIb5W4ZUFxMDaRkAg65gl9MvY2fH396riePW3RoKXDw== +"@tauri-apps/cli-win32-arm64-msvc@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.1.tgz#2f398fb17f4665fefe5b8cb21566cde7062236f5" + integrity sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg== -"@tauri-apps/cli-win32-ia32-msvc@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.9.1.tgz#b637cd436f129ef3ff6e83a4095c37b85f308a27" - integrity sha512-fSATtJDc0fNjVB6ystyi8NbwhNFk8i8E05h6KrsC8Fio5eaJIJvPCbC9pdrPl6kkxN1X7fj25ErBbgfqgcK8Fg== +"@tauri-apps/cli-win32-ia32-msvc@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.1.tgz#ffcbc2fa6a1d71f0b38672ea3468c07581f5cfaf" + integrity sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw== -"@tauri-apps/cli-win32-x64-msvc@2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.9.1.tgz#d7c8512dd70f9aa8203f2011322542ec11f76b66" - integrity sha512-/JHlOzpUDhjBOO9w167bcYxfJbcMQv7ykS/Y07xjtcga8np0rzUzVGWYmLMH7orKcDMC7wjhheEW1x8cbGma/Q== +"@tauri-apps/cli-win32-x64-msvc@2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.1.tgz#75a3842ecab5e13b15ad2eead4c1b9cc3916e78d" + integrity sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg== -"@tauri-apps/cli@^2.4.0": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.9.1.tgz#cdc1cc6a005fef017b2a8f1a96d32537afc74579" - integrity sha512-kKi2/WWsNXKoMdatBl4xrT7e1Ce27JvsetBVfWuIb6D3ep/Y0WO5SIr70yarXOSWam8NyDur4ipzjZkg6m7VDg== +"@tauri-apps/cli@^2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/cli/-/cli-2.10.1.tgz#d9b0290f03d569e10eb349a0bb72f448880bf9d7" + integrity sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g== optionalDependencies: - "@tauri-apps/cli-darwin-arm64" "2.9.1" - "@tauri-apps/cli-darwin-x64" "2.9.1" - "@tauri-apps/cli-linux-arm-gnueabihf" "2.9.1" - "@tauri-apps/cli-linux-arm64-gnu" "2.9.1" - "@tauri-apps/cli-linux-arm64-musl" "2.9.1" - "@tauri-apps/cli-linux-riscv64-gnu" "2.9.1" - "@tauri-apps/cli-linux-x64-gnu" "2.9.1" - "@tauri-apps/cli-linux-x64-musl" "2.9.1" - "@tauri-apps/cli-win32-arm64-msvc" "2.9.1" - "@tauri-apps/cli-win32-ia32-msvc" "2.9.1" - "@tauri-apps/cli-win32-x64-msvc" "2.9.1" + "@tauri-apps/cli-darwin-arm64" "2.10.1" + "@tauri-apps/cli-darwin-x64" "2.10.1" + "@tauri-apps/cli-linux-arm-gnueabihf" "2.10.1" + "@tauri-apps/cli-linux-arm64-gnu" "2.10.1" + "@tauri-apps/cli-linux-arm64-musl" "2.10.1" + "@tauri-apps/cli-linux-riscv64-gnu" "2.10.1" + "@tauri-apps/cli-linux-x64-gnu" "2.10.1" + "@tauri-apps/cli-linux-x64-musl" "2.10.1" + "@tauri-apps/cli-win32-arm64-msvc" "2.10.1" + "@tauri-apps/cli-win32-ia32-msvc" "2.10.1" + "@tauri-apps/cli-win32-x64-msvc" "2.10.1" -"@tauri-apps/plugin-clipboard-manager@^2.2.2": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.3.0.tgz#1d0dea11aa88970cf94a153a052cb0d6634bcacd" - integrity sha512-81NOBA2P+OTY8RLkBwyl9ZR/0CeggLub4F6zxcxUIfFOAqtky7J61+K/MkH2SC1FMxNBxrX0swDuKvkjkHadlA== - dependencies: - "@tauri-apps/api" "^2.6.0" - -"@tauri-apps/plugin-opener@^2.2.6": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-opener/-/plugin-opener-2.5.0.tgz#f4ed07559065616bf14012686f5273070a39e003" - integrity sha512-B0LShOYae4CZjN8leiNDbnfjSrTwoZakqKaWpfoH6nXiJwt6Rgj6RnVIffG3DoJiKsffRhMkjmBV9VeilSb4TA== +"@tauri-apps/plugin-clipboard-manager@^2.3.2": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.3.2.tgz#41def8a008eca94fd5abe2915d26a9c4b6ce54ca" + integrity sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ== dependencies: "@tauri-apps/api" "^2.8.0" -"@tauri-apps/plugin-shell@^2.2.1": +"@tauri-apps/plugin-opener@^2.5.3": + version "2.5.3" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-opener/-/plugin-opener-2.5.3.tgz#09f8fe143567839cc491f4f8fde21caa0f1a8b89" + integrity sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ== + dependencies: + "@tauri-apps/api" "^2.8.0" + +"@tauri-apps/plugin-process@^2.3.1": version "2.3.1" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-shell/-/plugin-shell-2.3.1.tgz#e92fb07e7bcf48ba647e5c8ef78e2ea8b469db15" - integrity sha512-jjs2WGDO/9z2pjNlydY/F5yYhNsscv99K5lCmU5uKjsVvQ3dRlDhhtVYoa4OLDmktLtQvgvbQjCFibMl6tgGfw== + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-process/-/plugin-process-2.3.1.tgz#fff77aa7550c9c5347689c859d581f88287bf7ae" + integrity sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA== dependencies: "@tauri-apps/api" "^2.8.0" -"@tauri-apps/plugin-updater@^2.0.0": - version "2.9.0" - resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-updater/-/plugin-updater-2.9.0.tgz#ba50b4e644fe19fa6f8465bd86d48119b0d3f41c" - integrity sha512-j++sgY8XpeDvzImTrzWA08OqqGqgkNyxczLD7FjNJJx/uXxMZFz5nDcfkyoI/rCjYuj2101Tci/r/HFmOmoxCg== +"@tauri-apps/plugin-shell@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-shell/-/plugin-shell-2.3.5.tgz#ec7b5c2aedaae9e4641cd3ccf1106e443e193919" + integrity sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg== dependencies: - "@tauri-apps/api" "^2.6.0" + "@tauri-apps/api" "^2.10.1" + +"@tauri-apps/plugin-updater@^2.10.1": + version "2.10.1" + resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-updater/-/plugin-updater-2.10.1.tgz#ea0efd766890394b6c719b9fc21de7da0029c69c" + integrity sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA== + dependencies: + "@tauri-apps/api" "^2.10.1" "@tauri-apps/tauri-forage@^1.0.0-beta.2": version "1.0.0-beta.2" @@ -6144,7 +6513,21 @@ tweetnacl "^1.0.1" tweetnacl-util "^0.15.0" -"@testing-library/dom@^8.0.0", "@testing-library/dom@^8.3.0": +"@testing-library/dom@^10.4.1": + version "10.4.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.1.tgz#d444f8a889e9a46e9a3b4f3b88e0fcb3efb6cf95" + integrity sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + picocolors "1.1.1" + pretty-format "^27.0.2" + +"@testing-library/dom@^8.3.0": version "8.20.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g== @@ -6158,29 +6541,24 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/jest-dom@^5.14.1": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz#5e97c8f9a15ccf4656da00fecab505728de81e0c" - integrity sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg== +"@testing-library/jest-dom@^6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz#7613a04e146dd2976d24ddf019730d57a89d56c2" + integrity sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA== dependencies: - "@adobe/css-tools" "^4.0.1" - "@babel/runtime" "^7.9.2" - "@types/testing-library__jest-dom" "^5.9.1" + "@adobe/css-tools" "^4.4.0" aria-query "^5.0.0" - chalk "^3.0.0" css.escape "^1.5.1" - dom-accessibility-api "^0.5.6" - lodash "^4.17.15" + dom-accessibility-api "^0.6.3" + picocolors "^1.1.1" redent "^3.0.0" -"@testing-library/react@^12.0.0": - version "12.1.5" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" - integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== +"@testing-library/react@^16.3.2": + version "16.3.2" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.2.tgz#672883b7acb8e775fc0492d9e9d25e06e89786d0" + integrity sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.0.0" - "@types/react-dom" "<18.0.0" "@testing-library/user-event@^13.2.1": version "13.5.0" @@ -6229,7 +6607,7 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.20.5": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -6559,14 +6937,6 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*": - version "30.0.0" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-30.0.0.tgz#5e85ae568006712e4ad66f25433e9bdac8801f1d" - integrity sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA== - dependencies: - expect "^30.0.0" - pretty-format "^30.0.0" - "@types/jest@^27.0.1": version "27.5.2" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c" @@ -6575,6 +6945,14 @@ jest-matcher-utils "^27.0.0" pretty-format "^27.0.0" +"@types/jest@^29.5.14": + version "29.5.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -6668,6 +7046,13 @@ dependencies: undici-types "~6.21.0" +"@types/node@^22.15.29": + version "22.19.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.17.tgz#09c71fb34ba2510f8ac865361b1fcb9552b8a581" + integrity sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q== + dependencies: + undici-types "~6.21.0" + "@types/normalize-package-data@^2.4.0": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" @@ -6722,16 +7107,16 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@<18.0.0": - version "17.0.26" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.26.tgz#fa7891ba70fd39ddbaa7e85b6ff9175bb546bc1b" - integrity sha512-Z+2VcYXJwOqQ79HreLU/1fyQ88eXSSFh6I3JdrEHQIfYSI0kCQpTGvOrbE6jFGGYXKsHuwY9tBa/w5Uo6KzrEg== - "@types/react-dom@^18", "@types/react-dom@^18.0.10": version "18.3.7" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.7.tgz#b89ddf2cd83b4feafcc4e2ea41afdfb95a0d194f" integrity sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ== +"@types/react-dom@^19.2.3": + version "19.2.3" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" + integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== + "@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.11", "@types/react-transition-group@^4.4.12": version "4.4.12" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" @@ -6752,6 +7137,13 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/react@^19.2.14": + version "19.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.14.tgz#39604929b5e3957e3a6fa0001dafb17c7af70bad" + integrity sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w== + dependencies: + csstype "^3.2.2" + "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -6815,13 +7207,6 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.12.tgz#bc2cab12e87978eee89fb21576b670350d6d86ab" integrity sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q== -"@types/testing-library__jest-dom@^5.9.1": - version "5.14.9" - resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466" - integrity sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw== - dependencies: - "@types/jest" "*" - "@types/trusted-types@^2.0.7": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" @@ -6944,6 +7329,20 @@ natural-compare "^1.4.0" ts-api-utils "^2.1.0" +"@typescript-eslint/eslint-plugin@^8.56.1": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz#a6882a6a328e1259cff259fdb03184245ef06191" + integrity sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw== + dependencies: + "@eslint-community/regexpp" "^4.12.2" + "@typescript-eslint/scope-manager" "8.58.2" + "@typescript-eslint/type-utils" "8.58.2" + "@typescript-eslint/utils" "8.58.2" + "@typescript-eslint/visitor-keys" "8.58.2" + ignore "^7.0.5" + natural-compare "^1.4.0" + ts-api-utils "^2.5.0" + "@typescript-eslint/experimental-utils@^5.3.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741" @@ -6972,6 +7371,17 @@ "@typescript-eslint/visitor-keys" "8.46.2" debug "^4.3.4" +"@typescript-eslint/parser@^8.56.1": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.58.2.tgz#b267545e4bd515d896fe1f3a5b6f334fa6aa0026" + integrity sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg== + dependencies: + "@typescript-eslint/scope-manager" "8.58.2" + "@typescript-eslint/types" "8.58.2" + "@typescript-eslint/typescript-estree" "8.58.2" + "@typescript-eslint/visitor-keys" "8.58.2" + debug "^4.4.3" + "@typescript-eslint/project-service@8.46.2": version "8.46.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.46.2.tgz#ab2f02a0de4da6a7eeb885af5e059be57819d608" @@ -6981,6 +7391,15 @@ "@typescript-eslint/types" "^8.46.2" debug "^4.3.4" +"@typescript-eslint/project-service@8.58.2": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.58.2.tgz#8c980249100e21b87baba0ca10880fdf893e0a8e" + integrity sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.58.2" + "@typescript-eslint/types" "^8.58.2" + debug "^4.4.3" + "@typescript-eslint/scope-manager@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" @@ -6997,11 +7416,24 @@ "@typescript-eslint/types" "8.46.2" "@typescript-eslint/visitor-keys" "8.46.2" +"@typescript-eslint/scope-manager@8.58.2": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz#aa73784d78f117940e83f71705af07ba695cd60c" + integrity sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q== + dependencies: + "@typescript-eslint/types" "8.58.2" + "@typescript-eslint/visitor-keys" "8.58.2" + "@typescript-eslint/tsconfig-utils@8.46.2", "@typescript-eslint/tsconfig-utils@^8.46.2": version "8.46.2" resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz#d110451cb93bbd189865206ea37ef677c196828c" integrity sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag== +"@typescript-eslint/tsconfig-utils@8.58.2", "@typescript-eslint/tsconfig-utils@^8.58.2": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz#fa13f96432c9348bf87f6f44826def585fad7bca" + integrity sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A== + "@typescript-eslint/type-utils@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" @@ -7023,6 +7455,17 @@ debug "^4.3.4" ts-api-utils "^2.1.0" +"@typescript-eslint/type-utils@8.58.2": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz#024eb1dd597f8a34cb22d8d9ab32da857bc9a817" + integrity sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg== + dependencies: + "@typescript-eslint/types" "8.58.2" + "@typescript-eslint/typescript-estree" "8.58.2" + "@typescript-eslint/utils" "8.58.2" + debug "^4.4.3" + ts-api-utils "^2.5.0" + "@typescript-eslint/types@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" @@ -7033,6 +7476,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.46.2.tgz#2bad7348511b31e6e42579820e62b73145635763" integrity sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ== +"@typescript-eslint/types@8.58.2", "@typescript-eslint/types@^8.58.2": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.58.2.tgz#3ab8051de0f19a46ddefb0749d0f7d82974bd57c" + integrity sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ== + "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" @@ -7062,6 +7510,21 @@ semver "^7.6.0" ts-api-utils "^2.1.0" +"@typescript-eslint/typescript-estree@8.58.2": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz#b1beb1f959385b341cc76f0aebbf028e23dfdb8b" + integrity sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw== + dependencies: + "@typescript-eslint/project-service" "8.58.2" + "@typescript-eslint/tsconfig-utils" "8.58.2" + "@typescript-eslint/types" "8.58.2" + "@typescript-eslint/visitor-keys" "8.58.2" + debug "^4.4.3" + minimatch "^10.2.2" + semver "^7.7.3" + tinyglobby "^0.2.15" + ts-api-utils "^2.5.0" + "@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" @@ -7086,6 +7549,16 @@ "@typescript-eslint/types" "8.46.2" "@typescript-eslint/typescript-estree" "8.46.2" +"@typescript-eslint/utils@8.58.2": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.58.2.tgz#27165554a02d1ff57d98262fa92060498dabc8b3" + integrity sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA== + dependencies: + "@eslint-community/eslint-utils" "^4.9.1" + "@typescript-eslint/scope-manager" "8.58.2" + "@typescript-eslint/types" "8.58.2" + "@typescript-eslint/typescript-estree" "8.58.2" + "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" @@ -7102,12 +7575,20 @@ "@typescript-eslint/types" "8.46.2" eslint-visitor-keys "^4.2.1" +"@typescript-eslint/visitor-keys@8.58.2": + version "8.58.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz#9ed699eaa9b5720b6b6b6f9c16e6c7d4cd32b276" + integrity sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA== + dependencies: + "@typescript-eslint/types" "8.58.2" + eslint-visitor-keys "^5.0.0" + "@uidotdev/usehooks@^2.4.1": version "2.4.1" resolved "https://registry.yarnpkg.com/@uidotdev/usehooks/-/usehooks-2.4.1.tgz#4b733eaeae09a7be143c6c9ca158b56cc1ea75bf" integrity sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg== -"@ungap/structured-clone@^1.0.0": +"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0", "@ungap/structured-clone@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== @@ -7733,6 +8214,11 @@ acorn@^8.15.0, acorn@^8.2.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== +acorn@^8.9.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -7866,7 +8352,7 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== -ansi-escapes@^4.2.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -7995,6 +8481,13 @@ aria-query@5.1.3: dependencies: deep-equal "^2.0.5" +aria-query@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + aria-query@^5.0.0, aria-query@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" @@ -8298,6 +8791,19 @@ axobject-query@^4.1.0: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== +babel-jest@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.3.0.tgz#3ff5553fa3bcbb8738d2d7335a4dbdc3bd1a0eb5" + integrity sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ== + dependencies: + "@jest/transform" "30.3.0" + "@types/babel__core" "^7.20.5" + babel-plugin-istanbul "^7.0.1" + babel-preset-jest "30.3.0" + chalk "^4.1.2" + graceful-fs "^4.2.11" + slash "^3.0.0" + babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" @@ -8361,6 +8867,24 @@ babel-plugin-istanbul@^6.0.0, babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" +babel-plugin-istanbul@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz#d8b518c8ea199364cf84ccc82de89740236daf92" + integrity sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-instrument "^6.0.2" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz#235ad714a45c18b12566becf439e1c604e277015" + integrity sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg== + dependencies: + "@types/babel__core" "^7.20.5" + babel-plugin-jest-hoist@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" @@ -8440,7 +8964,7 @@ babel-plugin-root-import@^6.6.0: dependencies: slash "^3.0.0" -babel-preset-current-node-syntax@^1.0.0: +babel-preset-current-node-syntax@^1.0.0, babel-preset-current-node-syntax@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== @@ -8461,6 +8985,14 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" +babel-preset-jest@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz#21cf3d19a6f5e9924426c879ee0b7f092636d043" + integrity sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ== + dependencies: + babel-plugin-jest-hoist "30.3.0" + babel-preset-current-node-syntax "^1.2.0" + babel-preset-jest@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" @@ -8484,6 +9016,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + base-x@^3.0.2, base-x@^3.0.6: version "3.0.11" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.11.tgz#40d80e2a1aeacba29792ccc6c5354806421287ff" @@ -8721,6 +9258,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +brace-expansion@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" + integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== + dependencies: + balanced-match "^4.0.2" + braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -8832,7 +9376,7 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.21.4, browserslist@^4 node-releases "^2.0.27" update-browserslist-db "^1.2.0" -bs-logger@0.x: +bs-logger@0.x, bs-logger@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== @@ -9082,7 +9626,7 @@ call-me-maybe@^1.0.1: resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== -callsites@^3.0.0: +callsites@^3.0.0, callsites@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== @@ -9127,7 +9671,7 @@ camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: +camelcase@^6.2.0, camelcase@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -9198,14 +9742,6 @@ chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -9344,6 +9880,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d" integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q== +cjs-module-lexer@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca" + integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -9493,7 +10034,7 @@ collapse-white-space@^1.0.2: resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== -collect-v8-coverage@^1.0.0: +collect-v8-coverage@^1.0.0, collect-v8-coverage@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz#cc1f01eb8d02298cbc9a437c74c70ab4e5210b80" integrity sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== @@ -10021,7 +10562,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.3, cross-spawn@^7.0.5, cross-spawn@^7.0.6: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -10363,6 +10904,11 @@ csstype@^3.0.2, csstype@^3.0.7, csstype@^3.1.3: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -10629,6 +11175,11 @@ dedent@^1.5.3: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== +dedent@^1.6.0: + version "1.7.2" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.2.tgz#34e2264ab538301e27cf7b07bf2369c19baa8dd9" + integrity sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA== + deep-equal@^2.0.5: version "2.2.3" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" @@ -10663,7 +11214,7 @@ deep-object-diff@^1.1.9: resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.9.tgz#6df7ef035ad6a0caa44479c536ed7b02570f4595" integrity sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA== -deepmerge@^4.2.2: +deepmerge@^4.2.2, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== @@ -10791,7 +11342,7 @@ deprecation@^2.0.0: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -dequal@^2.0.0: +dequal@^2.0.0, dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== @@ -10831,7 +11382,7 @@ detect-libc@^2.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== -detect-newline@^3.0.0: +detect-newline@^3.0.0, detect-newline@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== @@ -10917,11 +11468,16 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: +dom-accessibility-api@^0.5.9: version "0.5.16" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -11141,6 +11697,11 @@ elliptic@^6.4.0, elliptic@^6.5.3, elliptic@^6.5.4, elliptic@^6.6.1: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -11673,6 +12234,14 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-scope@^8.3.0: version "8.4.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" @@ -11681,7 +12250,7 @@ eslint-scope@^8.3.0: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== @@ -11691,6 +12260,55 @@ eslint-visitor-keys@^4.2.0, eslint-visitor-keys@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== +eslint-visitor-keys@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be" + integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== + +eslint@^8.57.1: + version "8.57.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" + integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== + 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" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + eslint@^9, eslint@^9.26.0: version "9.26.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.26.0.tgz#978fe029adc2aceed28ab437bca876e83461c3b4" @@ -11743,12 +12361,21 @@ espree@^10.0.1, espree@^10.3.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.2.1" +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.5.0: +esquery@^1.4.2, esquery@^1.5.0: version "1.7.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== @@ -11874,6 +12501,11 @@ execa@^5.0.0, execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +exit-x@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/exit-x/-/exit-x-0.2.2.tgz#1f9052de3b8d99a696b10dad5bced9bdd5c3aa64" + integrity sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ== + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -11892,6 +12524,18 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expect@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-30.3.0.tgz#1b82111517d1ab030f3db0cf1b4061c8aa644f61" + integrity sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q== + dependencies: + "@jest/expect-utils" "30.3.0" + "@jest/get-type" "30.1.0" + jest-matcher-utils "30.3.0" + jest-message-util "30.3.0" + jest-mock "30.3.0" + jest-util "30.3.0" + expect@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" @@ -11902,17 +12546,16 @@ expect@^27.5.1: jest-matcher-utils "^27.5.1" jest-message-util "^27.5.1" -expect@^30.0.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-30.2.0.tgz#d4013bed267013c14bc1199cec8aa57cee9b5869" - integrity sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw== +expect@^29.0.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: - "@jest/expect-utils" "30.2.0" - "@jest/get-type" "30.1.0" - jest-matcher-utils "30.2.0" - jest-message-util "30.2.0" - jest-mock "30.2.0" - jest-util "30.2.0" + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" exponential-backoff@^3.1.1: version "3.1.3" @@ -12085,7 +12728,7 @@ fast-json-parse@^1.0.3: resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -12145,7 +12788,7 @@ faye-websocket@^0.11.3: dependencies: websocket-driver ">=0.5.1" -fb-watchman@^2.0.0: +fb-watchman@^2.0.0, fb-watchman@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== @@ -12174,6 +12817,13 @@ figures@3.2.0, figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + file-entry-cache@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" @@ -12598,7 +13248,7 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@^2.1.2, fsevents@^2.3.2, fsevents@^2.3.3, fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -12883,6 +13533,18 @@ glob@^10.2.2: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" +glob@^10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -12924,6 +13586,13 @@ global@^4.4.0: min-document "^2.19.0" process "^0.11.10" +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + globals@^14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" @@ -13032,6 +13701,18 @@ handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" +handlebars@^4.7.9: + version "4.7.9" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.9.tgz#6f139082ab58dc4e5a0e51efe7db5ae890d56a0f" + integrity sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + hard-rejection@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" @@ -13654,7 +14335,7 @@ ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -ignore@^7.0.0: +ignore@^7.0.0, ignore@^7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== @@ -13685,7 +14366,7 @@ import-local@3.1.0: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" -import-local@^3.0.2: +import-local@^3.0.2, import-local@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== @@ -14096,7 +14777,7 @@ is-function@^1.0.2: resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== -is-generator-fn@^2.0.0: +is-generator-fn@^2.0.0, is-generator-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== @@ -14210,6 +14891,11 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -14463,6 +15149,17 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== + 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" + istanbul-lib-report@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" @@ -14481,6 +15178,15 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" +istanbul-lib-source-maps@^5.0.0: + version "5.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== + dependencies: + "@jridgewell/trace-mapping" "^0.3.23" + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + istanbul-reports@^3.1.3, istanbul-reports@^3.1.4: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" @@ -14539,6 +15245,15 @@ jake@^10.8.5: filelist "^1.0.4" picocolors "^1.1.1" +jest-changed-files@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.3.0.tgz#055849df695f9a9fcde0ae44024f815bbc627f3a" + integrity sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA== + dependencies: + execa "^5.1.1" + jest-util "30.3.0" + p-limit "^3.1.0" + jest-changed-files@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" @@ -14548,6 +15263,32 @@ jest-changed-files@^27.5.1: execa "^5.0.0" throat "^6.0.1" +jest-circus@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.3.0.tgz#153614c11ab35867f371bd93496ecb9690b92077" + integrity sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA== + dependencies: + "@jest/environment" "30.3.0" + "@jest/expect" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/types" "30.3.0" + "@types/node" "*" + chalk "^4.1.2" + co "^4.6.0" + dedent "^1.6.0" + is-generator-fn "^2.1.0" + jest-each "30.3.0" + jest-matcher-utils "30.3.0" + jest-message-util "30.3.0" + jest-runtime "30.3.0" + jest-snapshot "30.3.0" + jest-util "30.3.0" + p-limit "^3.1.0" + pretty-format "30.3.0" + pure-rand "^7.0.0" + slash "^3.0.0" + stack-utils "^2.0.6" + jest-circus@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" @@ -14573,6 +15314,22 @@ jest-circus@^27.5.1: stack-utils "^2.0.3" throat "^6.0.1" +jest-cli@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.3.0.tgz#5ed75a337f486a1f1c5acbb2de8acddb106ead6c" + integrity sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw== + dependencies: + "@jest/core" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/types" "30.3.0" + chalk "^4.1.2" + exit-x "^0.2.2" + import-local "^3.2.0" + jest-config "30.3.0" + jest-util "30.3.0" + jest-validate "30.3.0" + yargs "^17.7.2" + jest-cli@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" @@ -14591,6 +15348,35 @@ jest-cli@^27.5.1: prompts "^2.0.1" yargs "^16.2.0" +jest-config@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.3.0.tgz#b969e0aaaf5964419e62953bb712c16d15972425" + integrity sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w== + dependencies: + "@babel/core" "^7.27.4" + "@jest/get-type" "30.1.0" + "@jest/pattern" "30.0.1" + "@jest/test-sequencer" "30.3.0" + "@jest/types" "30.3.0" + babel-jest "30.3.0" + chalk "^4.1.2" + ci-info "^4.2.0" + deepmerge "^4.3.1" + glob "^10.5.0" + graceful-fs "^4.2.11" + jest-circus "30.3.0" + jest-docblock "30.2.0" + jest-environment-node "30.3.0" + jest-regex-util "30.0.1" + jest-resolve "30.3.0" + jest-runner "30.3.0" + jest-util "30.3.0" + jest-validate "30.3.0" + parse-json "^5.2.0" + pretty-format "30.3.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-config@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" @@ -14621,17 +15407,17 @@ jest-config@^27.5.1: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.2.0.tgz#e3ec3a6ea5c5747f605c9e874f83d756cba36825" - integrity sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A== +jest-diff@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.3.0.tgz#e0a4c84ef350ffd790ffd5b0016acabeecf5f759" + integrity sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ== dependencies: - "@jest/diff-sequences" "30.0.1" + "@jest/diff-sequences" "30.3.0" "@jest/get-type" "30.1.0" chalk "^4.1.2" - pretty-format "30.2.0" + pretty-format "30.3.0" -"jest-diff@>=29.4.3 < 30", jest-diff@^29.4.1: +"jest-diff@>=29.4.3 < 30", jest-diff@^29.4.1, jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== @@ -14651,6 +15437,13 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-docblock@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-30.2.0.tgz#42cd98d69f887e531c7352309542b1ce4ee10256" + integrity sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA== + dependencies: + detect-newline "^3.1.0" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -14658,6 +15451,17 @@ jest-docblock@^27.5.1: dependencies: detect-newline "^3.0.0" +jest-each@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.3.0.tgz#faa7229bf7a9fa6426dc604057a7d2a173493b1e" + integrity sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA== + dependencies: + "@jest/get-type" "30.1.0" + "@jest/types" "30.3.0" + chalk "^4.1.2" + jest-util "30.3.0" + pretty-format "30.3.0" + jest-each@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" @@ -14682,6 +15486,19 @@ jest-environment-jsdom@^27.5.1: jest-util "^27.5.1" jsdom "^16.6.0" +jest-environment-node@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.3.0.tgz#aa8a57c5d0c4af0f8b1f7403ba737fec6b3aabbe" + integrity sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ== + dependencies: + "@jest/environment" "30.3.0" + "@jest/fake-timers" "30.3.0" + "@jest/types" "30.3.0" + "@types/node" "*" + jest-mock "30.3.0" + jest-util "30.3.0" + jest-validate "30.3.0" + jest-environment-node@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" @@ -14704,6 +15521,24 @@ jest-get-type@^29.6.3: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== +jest-haste-map@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.3.0.tgz#1ea6843e6e45c077d91270666a4fcba958c24cd5" + integrity sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA== + dependencies: + "@jest/types" "30.3.0" + "@types/node" "*" + anymatch "^3.1.3" + fb-watchman "^2.0.2" + graceful-fs "^4.2.11" + jest-regex-util "30.0.1" + jest-util "30.3.0" + jest-worker "30.3.0" + picomatch "^4.0.3" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.3" + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -14768,6 +15603,14 @@ jest-jasmine2@^27.5.1: pretty-format "^27.5.1" throat "^6.0.1" +jest-leak-detector@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz#a695a851e353f517a554a2f5c91c2742fc131c98" + integrity sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ== + dependencies: + "@jest/get-type" "30.1.0" + pretty-format "30.3.0" + jest-leak-detector@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" @@ -14776,15 +15619,15 @@ jest-leak-detector@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-matcher-utils@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz#69a0d4c271066559ec8b0d8174829adc3f23a783" - integrity sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg== +jest-matcher-utils@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz#d6c739fec1ecd33809f2d2b1348f6ab01d2f2493" + integrity sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA== dependencies: "@jest/get-type" "30.1.0" chalk "^4.1.2" - jest-diff "30.2.0" - pretty-format "30.2.0" + jest-diff "30.3.0" + pretty-format "30.3.0" jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: version "27.5.1" @@ -14796,18 +15639,28 @@ jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" -jest-message-util@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.2.0.tgz#fc97bf90d11f118b31e6131e2b67fc4f39f92152" - integrity sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw== +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.3.0.tgz#4d723544d36890ba862ac3961db52db5b0d1ba39" + integrity sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw== dependencies: "@babel/code-frame" "^7.27.1" - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" "@types/stack-utils" "^2.0.3" chalk "^4.1.2" graceful-fs "^4.2.11" - micromatch "^4.0.8" - pretty-format "30.2.0" + picomatch "^4.0.3" + pretty-format "30.3.0" slash "^3.0.0" stack-utils "^2.0.6" @@ -14826,14 +15679,29 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.2.0.tgz#69f991614eeb4060189459d3584f710845bff45e" - integrity sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: - "@jest/types" "30.2.0" + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.3.0.tgz#e0fa4184a596a6c4fdec53d4f412158418923747" + integrity sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog== + dependencies: + "@jest/types" "30.3.0" "@types/node" "*" - jest-util "30.2.0" + jest-util "30.3.0" jest-mock@^27.0.6, jest-mock@^27.5.1: version "27.5.1" @@ -14843,7 +15711,7 @@ jest-mock@^27.0.6, jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" -jest-pnp-resolver@^1.2.2: +jest-pnp-resolver@^1.2.2, jest-pnp-resolver@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== @@ -14863,6 +15731,14 @@ jest-regex-util@^27.5.1: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== +jest-resolve-dependencies@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz#4d638c9f0d93a62a6ed25dec874bfd7e756c8ce5" + integrity sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw== + dependencies: + jest-regex-util "30.0.1" + jest-snapshot "30.3.0" + jest-resolve-dependencies@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" @@ -14872,6 +15748,20 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" +jest-resolve@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.3.0.tgz#b7bee9927279805b1b50715d2170a545553b87ff" + integrity sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g== + dependencies: + chalk "^4.1.2" + graceful-fs "^4.2.11" + jest-haste-map "30.3.0" + jest-pnp-resolver "^1.2.3" + jest-util "30.3.0" + jest-validate "30.3.0" + slash "^3.0.0" + unrs-resolver "^1.7.11" + jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" @@ -14888,6 +15778,34 @@ jest-resolve@^27.5.1: resolve.exports "^1.1.0" slash "^3.0.0" +jest-runner@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.3.0.tgz#fa970fc4e45d418ad7e7d581b24cac7af5944cb7" + integrity sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw== + dependencies: + "@jest/console" "30.3.0" + "@jest/environment" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" + "@types/node" "*" + chalk "^4.1.2" + emittery "^0.13.1" + exit-x "^0.2.2" + graceful-fs "^4.2.11" + jest-docblock "30.2.0" + jest-environment-node "30.3.0" + jest-haste-map "30.3.0" + jest-leak-detector "30.3.0" + jest-message-util "30.3.0" + jest-resolve "30.3.0" + jest-runtime "30.3.0" + jest-util "30.3.0" + jest-watcher "30.3.0" + jest-worker "30.3.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runner@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" @@ -14915,6 +15833,34 @@ jest-runner@^27.5.1: source-map-support "^0.5.6" throat "^6.0.1" +jest-runtime@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.3.0.tgz#1a9bec7a9b68db12dfe4136bbe41ab883ea2c996" + integrity sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng== + dependencies: + "@jest/environment" "30.3.0" + "@jest/fake-timers" "30.3.0" + "@jest/globals" "30.3.0" + "@jest/source-map" "30.0.1" + "@jest/test-result" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" + "@types/node" "*" + chalk "^4.1.2" + cjs-module-lexer "^2.1.0" + collect-v8-coverage "^1.0.2" + glob "^10.5.0" + graceful-fs "^4.2.11" + jest-haste-map "30.3.0" + jest-message-util "30.3.0" + jest-mock "30.3.0" + jest-regex-util "30.0.1" + jest-resolve "30.3.0" + jest-snapshot "30.3.0" + jest-util "30.3.0" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-runtime@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" @@ -14959,6 +15905,33 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" +jest-snapshot@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.3.0.tgz#6e7ea75069dda86e36311a0f73189e830d4f51ad" + integrity sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ== + dependencies: + "@babel/core" "^7.27.4" + "@babel/generator" "^7.27.5" + "@babel/plugin-syntax-jsx" "^7.27.1" + "@babel/plugin-syntax-typescript" "^7.27.1" + "@babel/types" "^7.27.3" + "@jest/expect-utils" "30.3.0" + "@jest/get-type" "30.1.0" + "@jest/snapshot-utils" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" + babel-preset-current-node-syntax "^1.2.0" + chalk "^4.1.2" + expect "30.3.0" + graceful-fs "^4.2.11" + jest-diff "30.3.0" + jest-matcher-utils "30.3.0" + jest-message-util "30.3.0" + jest-util "30.3.0" + pretty-format "30.3.0" + semver "^7.7.2" + synckit "^0.11.8" + jest-snapshot@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" @@ -14987,17 +15960,17 @@ jest-snapshot@^27.5.1: pretty-format "^27.5.1" semver "^7.3.2" -jest-util@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.2.0.tgz#5142adbcad6f4e53c2776c067a4db3c14f913705" - integrity sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA== +jest-util@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.3.0.tgz#95a4fbacf2dac20e768e2f1744b70519f2ba7980" + integrity sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg== dependencies: - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" "@types/node" "*" chalk "^4.1.2" ci-info "^4.2.0" graceful-fs "^4.2.11" - picomatch "^4.0.2" + picomatch "^4.0.3" jest-util@^26.6.2: version "26.6.2" @@ -15035,6 +16008,18 @@ jest-util@^29.7.0: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-validate@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.3.0.tgz#215e11b8fcc5e2ca4b99ea5d730a5b4c969e4355" + integrity sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q== + dependencies: + "@jest/get-type" "30.1.0" + "@jest/types" "30.3.0" + camelcase "^6.3.0" + chalk "^4.1.2" + leven "^3.1.0" + pretty-format "30.3.0" + jest-validate@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" @@ -15047,6 +16032,20 @@ jest-validate@^27.5.1: leven "^3.1.0" pretty-format "^27.5.1" +jest-watcher@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.3.0.tgz#3afa1af355b9fe80f0261eb8a23981a315858596" + integrity sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w== + dependencies: + "@jest/test-result" "30.3.0" + "@jest/types" "30.3.0" + "@types/node" "*" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + emittery "^0.13.1" + jest-util "30.3.0" + string-length "^4.0.2" + jest-watcher@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" @@ -15060,6 +16059,17 @@ jest-watcher@^27.5.1: jest-util "^27.5.1" string-length "^4.0.1" +jest-worker@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.3.0.tgz#ae4dc1f1d93d0cba1415624fcedaec40ea764f14" + integrity sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ== + dependencies: + "@types/node" "*" + "@ungap/structured-clone" "^1.3.0" + jest-util "30.3.0" + merge-stream "^2.0.0" + supports-color "^8.1.1" + jest-worker@^26.5.0, jest-worker@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -15097,6 +16107,16 @@ jest@^27.1.0: import-local "^3.0.2" jest-cli "^27.5.1" +jest@^30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-30.3.0.tgz#6460b889dd805e9677400505f16f1d9b14c285a3" + integrity sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg== + dependencies: + "@jest/core" "30.3.0" + "@jest/types" "30.3.0" + import-local "^3.2.0" + jest-cli "30.3.0" + joi@^17.11.0: version "17.13.3" resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec" @@ -15927,7 +16947,7 @@ make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: dependencies: semver "^6.0.0" -make-error@1.x: +make-error@1.x, make-error@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -16648,6 +17668,13 @@ minimatch@^10.0.3: dependencies: "@isaacs/brace-expansion" "^5.0.0" +minimatch@^10.2.2: + version "10.2.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" + integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== + dependencies: + brace-expansion "^5.0.5" + minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -16655,6 +17682,13 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + minimatch@^5.0.1: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" @@ -17665,7 +18699,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -18084,26 +19118,31 @@ pbkdf2@^3.1.2, pbkdf2@^3.1.5: sha.js "^2.4.12" to-buffer "^1.2.1" +picocolors@1.1.1, picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picocolors@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== -picocolors@^1.0.0, picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.0, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -picomatch@^4.0.2, picomatch@^4.0.3: +picomatch@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== +picomatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + pidtree@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" @@ -18171,7 +19210,7 @@ pino@7.11.0: sonic-boom "^2.2.1" thread-stream "^0.15.1" -pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.6: +pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.6, pirates@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== @@ -18790,10 +19829,10 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -pretty-format@30.2.0, pretty-format@^30.0.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.2.0.tgz#2d44fe6134529aed18506f6d11509d8a62775ebe" - integrity sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA== +pretty-format@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.3.0.tgz#e977eed4bcd1b6195faed418af8eac68b9ea1f29" + integrity sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ== dependencies: "@jest/schemas" "30.0.5" ansi-styles "^5.2.0" @@ -18808,7 +19847,7 @@ pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^29.7.0: +pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== @@ -19020,6 +20059,11 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +pure-rand@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-7.0.1.tgz#6f53a5a9e3e4a47445822af96821ca509ed37566" + integrity sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ== + qr.js@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" @@ -19222,7 +20266,7 @@ react-docgen@^5.0.0: node-dir "^0.1.10" strip-indent "^3.0.0" -react-dom@^18.0.0, react-dom@^18.2.0: +react-dom@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -19230,6 +20274,13 @@ react-dom@^18.0.0, react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.2" +react-dom@^19.2.0: + version "19.2.5" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.5.tgz#b8768b10837d0b8e9ca5b9e2d58dff3d880ea25e" + integrity sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag== + dependencies: + scheduler "^0.27.0" + react-element-to-jsx-string@^14.3.4: version "14.3.4" resolved "https://registry.yarnpkg.com/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz#709125bc72f06800b68f9f4db485f2c7d31218a8" @@ -19405,13 +20456,18 @@ react-world-flags@^1.6.0: svgo "^3.0.2" world-countries "^5.0.0" -react@^18.0.0, react@^18.2.0: +react@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== dependencies: loose-envify "^1.1.0" +react@^19.2.0: + version "19.2.5" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.5.tgz#c888ab8b8ef33e2597fae8bdb2d77edbdb42858b" + integrity sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA== + read-cmd-shim@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz#640a08b473a49043e394ae0c7a34dd822c73b9bb" @@ -20104,6 +21160,11 @@ scheduler@^0.23.2: dependencies: loose-envify "^1.1.0" +scheduler@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" + integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== + schema-utils@2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" @@ -20185,6 +21246,11 @@ semver@^6.0.0, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.7.2, semver@^7.7.3, semver@^7.7.4: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + send@0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" @@ -20610,6 +21676,14 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.16, source-map-support@^0.5.21, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -20856,7 +21930,7 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -string-length@^4.0.1: +string-length@^4.0.1, string-length@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== @@ -21181,7 +22255,7 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: +supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -21265,6 +22339,13 @@ synchronous-promise@^2.0.15: resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" integrity sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g== +synckit@^0.11.8: + version "0.11.12" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.12.tgz#abe74124264fbc00a48011b0d98bdc1cffb64a7b" + integrity sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ== + dependencies: + "@pkgr/core" "^0.2.9" + tabbable@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.3.0.tgz#2e0e6163935387cdeacd44e9334616ca0115a8d3" @@ -21416,6 +22497,11 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + thread-loader@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/thread-loader/-/thread-loader-3.0.4.tgz#c392e4c0241fbc80430eb680e4886819b504a31b" @@ -21503,6 +22589,14 @@ tinyglobby@^0.2.13: fdir "^6.5.0" picomatch "^4.0.3" +tinyglobby@^0.2.15: + version "0.2.16" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" + integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.4" + tinyrainbow@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5" @@ -21667,6 +22761,11 @@ ts-api-utils@^2.1.0: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== +ts-api-utils@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" + integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== + ts-dedent@^2.0.0, ts-dedent@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" @@ -21686,6 +22785,21 @@ ts-jest@^27.0.5: semver "7.x" yargs-parser "20.x" +ts-jest@^29.4.9: + version "29.4.9" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.9.tgz#47dc33d0f5c36bddcedd16afefae285e0b049d2d" + integrity sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ== + dependencies: + bs-logger "^0.2.6" + fast-json-stable-stringify "^2.1.0" + handlebars "^4.7.9" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.7.4" + type-fest "^4.41.0" + yargs-parser "^21.1.1" + ts-loader@^9.4.2, ts-loader@^9.4.4: version "9.5.4" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.5.4.tgz#44b571165c10fb5a90744aa5b7e119233c4f4585" @@ -21830,6 +22944,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + type-is@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" @@ -21909,7 +23028,7 @@ typeforce@^1.11.5: resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -"typescript@>=3 < 6", typescript@^5: +"typescript@>=3 < 6", typescript@^5, typescript@^5.9.3: version "5.9.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== @@ -22188,7 +23307,7 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -unrs-resolver@^1.6.2: +unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: version "1.11.1" resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz#be9cd8686c99ef53ecb96df2a473c64d304048a9" integrity sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg== @@ -22397,7 +23516,7 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" -v8-to-istanbul@^9.0.0: +v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1: version "9.3.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== @@ -22528,7 +23647,7 @@ w3c-xmlserializer@^5.0.0: dependencies: xml-name-validator "^5.0.0" -walker@^1.0.7, walker@~1.0.5: +walker@^1.0.7, walker@^1.0.8, walker@~1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -23063,7 +24182,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@5.0.1: +write-file-atomic@5.0.1, write-file-atomic@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== @@ -23222,7 +24341,7 @@ yargs@16.2.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.6.2: +yargs@^17.6.2, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==