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 c8c1b37414..412cadc6d7 100644 Binary files a/nym-wallet/src-tauri/icons/128x128.png and b/nym-wallet/src-tauri/icons/128x128.png differ diff --git a/nym-wallet/src-tauri/icons/128x128@2x.png b/nym-wallet/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000000..0617557567 Binary files /dev/null and b/nym-wallet/src-tauri/icons/128x128@2x.png differ diff --git a/nym-wallet/src-tauri/icons/32x32.png b/nym-wallet/src-tauri/icons/32x32.png index 76e4168cd7..6549f3a6b8 100644 Binary files a/nym-wallet/src-tauri/icons/32x32.png and b/nym-wallet/src-tauri/icons/32x32.png differ diff --git a/nym-wallet/src-tauri/icons/64x64.png b/nym-wallet/src-tauri/icons/64x64.png new file mode 100644 index 0000000000..d5c694a23a Binary files /dev/null and b/nym-wallet/src-tauri/icons/64x64.png differ diff --git a/nym-wallet/src-tauri/icons/app-icon-source.png b/nym-wallet/src-tauri/icons/app-icon-source.png new file mode 100644 index 0000000000..01d39e7fbc Binary files /dev/null and b/nym-wallet/src-tauri/icons/app-icon-source.png differ diff --git a/nym-wallet/src-tauri/icons/icon.icns b/nym-wallet/src-tauri/icons/icon.icns index d964ffd4da..30a1ed71bb 100644 Binary files a/nym-wallet/src-tauri/icons/icon.icns and b/nym-wallet/src-tauri/icons/icon.icns differ diff --git a/nym-wallet/src-tauri/icons/icon.ico b/nym-wallet/src-tauri/icons/icon.ico index c312b68a41..9b72bd77ec 100644 Binary files a/nym-wallet/src-tauri/icons/icon.ico and b/nym-wallet/src-tauri/icons/icon.ico differ diff --git a/nym-wallet/src-tauri/icons/icon.png b/nym-wallet/src-tauri/icons/icon.png index d964ffd4da..1c7a21d21e 100644 Binary files a/nym-wallet/src-tauri/icons/icon.png and b/nym-wallet/src-tauri/icons/icon.png differ diff --git a/nym-wallet/src-tauri/icons/tray_icon.png b/nym-wallet/src-tauri/icons/tray_icon.png index c8c1b37414..1a52bb5258 100644 Binary files a/nym-wallet/src-tauri/icons/tray_icon.png and b/nym-wallet/src-tauri/icons/tray_icon.png differ 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==