diff --git a/.env b/.env index 6073e57d..d42a352f 100644 --- a/.env +++ b/.env @@ -1,14 +1,28 @@ VITE_JAM_DEFAULT_THEME=dark +VITE_JAM_REPO_URL=https://github.com/joinmarket-webui/jam +VITE_JAM_DOCS_URL=https://jamdocs.org +VITE_JAM_MATRIX_URL=https://matrix.to/#/%23jam:bitcoin.kyoto +VITE_JAM_TELEGRAM_URL=https://t.me/JoinMarketWebUI +VITE_JAM_JMNG_REPO_URL=https://github.com/joinmarket-ng/joinmarket-ng -VITE_JM_API_BASE_URL=/api/v1 +VITE_JM_API_BASE_URL= VITE_JM_API_AUTH_TOKEN_EXPIRY_SECONDS=1800 VITE_JAM_JM_SESSION_REFRESH_INTERVAL=30000 VITE_JAM_RESCAN_PROGRESS_INTERVAL=10000 VITE_JAM_SEED_MODAL_TIMEOUT=30000 +VITE_JAM_SWEEP_DESTINATION_ADDRESSES_MIN_COUNT=3 +VITE_JAM_SWEEP_DESTINATION_ADDRESSES_DEFAULT_COUNT=3 + VITE_JM_WEBSOCKET_ENDPOINT_PATH=/jmws VITE_JM_WEBSOCKET_CONNECTION_HEALTHY_DURATION=1000 VITE_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_DURATION=3000 VITE_JM_WEBSOCKET_KEEPALIVE_MESSAGE_INTERVAL=30000 VITE_JM_WEBSOCKET_RECONNECT_INTERVAL_MIN=5000 VITE_JM_WEBSOCKET_RECONNECT_INTERVAL_MAX=60000 + +VITE_JAM_SWEEP_MIN_ROUNDING_CHANCE_PERCENT=10 +VITE_JAM_SWEEP_MAX_ROUNDING_CHANCE_PERCENT=90 + +VITE_JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS=true +VITE_JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS_DELAY=1000 diff --git a/.env.development b/.env.development index ea09cf5e..9b4b3df4 100644 --- a/.env.development +++ b/.env.development @@ -5,3 +5,13 @@ VITE_JM_WEBSOCKET_RECONNECT_INTERVAL_MIN=3000 VITE_JM_WEBSOCKET_RECONNECT_INTERVAL_MAX=30000 VITE_JAM_SEED_MODAL_TIMEOUT=21000 + +VITE_JAM_SWEEP_MAKER_SESSION_IDLE_MIN_TIMEOUT_SECONDS=59 +VITE_JAM_SWEEP_MAKER_SESSION_IDLE_TIMEOUT_SECONDS=59 + +VITE_JAM_SWEEP_DESTINATION_ADDRESSES_MIN_COUNT=1 +VITE_JAM_SWEEP_DESTINATION_ADDRESSES_DEFAULT_COUNT=3 + +VITE_JAM_SWEEP_MIN_MIN_NUMBER_OF_COLLABORATORS=1 + +VITE_JAM_RUNNING_SCHEDULE_POLLING_INTERVAL=5000 diff --git a/.env.test b/.env.test new file mode 100644 index 00000000..214a58a6 --- /dev/null +++ b/.env.test @@ -0,0 +1,7 @@ +VITE_JAM_SWEEP_DESTINATION_ADDRESSES_MIN_COUNT=1 +VITE_JAM_SWEEP_DESTINATION_ADDRESSES_DEFAULT_COUNT=3 + +VITE_JAM_SWEEP_MAKER_SESSION_IDLE_MIN_TIMEOUT_SECONDS=1 +VITE_JAM_SWEEP_MAKER_SESSION_IDLE_TIMEOUT_SECONDS=1 + +VITE_JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS_DELAY=1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 862ef55f..db7f7bd4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,14 +11,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: ['v26.3.0'] + node-version: ['v26.5.0'] steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup (Node.js ${{ matrix.node-version }}) - uses: actions/setup-node@v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} @@ -30,7 +30,7 @@ jobs: run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package-lock.json').packages['node_modules/@playwright/test'].version)")" >> $GITHUB_ENV - name: Cache Playwright - uses: actions/cache@v5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 id: playwright-cache with: path: | @@ -50,7 +50,7 @@ jobs: run: npm run format:check - name: Test - run: npm test -- --coverage.enabled + run: npm run test:coverage - name: Build run: npm run build @@ -59,4 +59,4 @@ jobs: # Set if: always() to also generate the report if tests are failing # Only works if you set `reportOnFailure: true` in vite config if: ${{ always() && hashFiles('coverage/coverage-summary.json') != '' }} - uses: davelosert/vitest-coverage-report-action@v2 + uses: davelosert/vitest-coverage-report-action@8b157684c6a6b259b97d45e72b44242865c0f6a5 # v2.12.2 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e89f19fa..ae329291 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,9 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v7 + with: + persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/storybook-build.yml b/.github/workflows/storybook-build.yml index 35594961..01d50de6 100644 --- a/.github/workflows/storybook-build.yml +++ b/.github/workflows/storybook-build.yml @@ -12,10 +12,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: .nvmrc diff --git a/.github/workflows/storybook-pages.yml b/.github/workflows/storybook-pages.yml index a0cdacb5..e23a3876 100644 --- a/.github/workflows/storybook-pages.yml +++ b/.github/workflows/storybook-pages.yml @@ -24,10 +24,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: .nvmrc @@ -38,13 +38,13 @@ jobs: run: npm run storybook:build-gh-pages - name: Configure Pages - uses: actions/configure-pages@v6 + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v5 + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: path: storybook-static - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v5 + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 diff --git a/.husky/pre-push b/.husky/pre-push index 72c4429b..9814e1a5 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1 +1 @@ -npm test +npm run test -- --bail=1 diff --git a/.nvmrc b/.nvmrc index 84a2f4a2..eacd4817 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v26.3.0 +v26.5.0 diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index ad3798d6..b6009ab1 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -2,11 +2,12 @@ import { Suspense, useEffect } from 'react' import type { Preview } from '@storybook/react-vite' import { QueryClientProvider, QueryClient } from '@tanstack/react-query' -import { initialize, mswLoader } from 'msw-storybook-addon' +import { mswLoader } from 'msw-storybook-addon/csf3' +import { setupWorker } from 'msw/browser' import { ThemeProvider } from 'next-themes' import { I18nextProvider } from 'react-i18next' import { MemoryRouter } from 'react-router-dom' -import { CoreTypes, GlobalTypes } from 'storybook/internal/csf' +import type { CoreTypes, GlobalTypes } from 'storybook/internal/csf' import { DEFAULT_VIEWPORT, MINIMAL_VIEWPORTS, INITIAL_VIEWPORTS } from 'storybook/viewport' import { JamDisplayContextProvider } from '../src/context/JamDisplayContextProvider' import { JamWalletInfoContextProvider } from '../src/context/JamWalletInfoContextProvider' @@ -17,15 +18,6 @@ import mswHandlers from './msw-handlers' // needed if you want to use msw on a subpath (e.g. github pages /) const mswServiceWorkerUrl = import.meta.env.STORYBOOK_MSW_SERVICE_WORKER_URL ?? '/mockServiceWorker.js' -// Initialize MSW (https://github.com/mswjs/msw-storybook-addon) -initialize({ - onUnhandledRequest: 'bypass', - serviceWorker: { - url: mswServiceWorkerUrl, - }, - quiet: true, -}) - const locales = [ { value: 'en', title: 'en' }, { value: 'fr', title: 'fr' }, @@ -85,12 +77,28 @@ const preview: Preview = { viewport: { options: { ...MINIMAL_VIEWPORTS, ...INITIAL_VIEWPORTS }, }, + // this approach is deprecated as of v3.0.0 + // but it seems the new approach does not work as expected (404 response for our handlers) + // revisit after next update (last checked 2026-07-30) + // see: https://github.com/mswjs/msw-storybook-addon/blob/v3.0.0/MIGRATION.md#parametersmsw-is-deprecated-in-favor-of-beforeeach msw: { handlers: mswHandlers, }, }, // Provide the MSW addon loader globally - loaders: [mswLoader], + loaders: [ + mswLoader(async () => { + const worker = setupWorker() + await worker.start({ + onUnhandledRequest: 'bypass', + serviceWorker: { + url: mswServiceWorkerUrl, + }, + quiet: true, + }) + return worker + }), + ], } const withTheme = (Story: React.ComponentType, context: GlobalContext) => { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index edc48612..c7fcaab5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,12 +5,12 @@ The aim of this document is to help you get setup for participating in all areas ## Understanding the Components Involved -Jam is a web UI for [JoinMarket](https://github.com/JoinMarket-Org/joinmarket-clientserver/) with focus on user-friendliness. -The web UI's purpose is to be a rather lightweight front end for the JoinMarket API. -To function, the web UI needs to connect to an instance of JoinMarket with [the API service](https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/docs/JSON-RPC-API-using-jmwalletd.md) running. +Jam is a web UI for [JoinMarket NG](https://github.com/joinmarket-ng/joinmarket-ng) with focus on user-friendliness. +The web UI's purpose is to be a rather lightweight front end for the JoinMarket NG API. +To function, the web UI needs to connect to an instance of JoinMarket NG with [the API service](https://joinmarket-ng.github.io/joinmarket-ng/README-jmwalletd/) running. You don't need to worry about that, though. -To ease development and testing, we provide a Docker setup that runs JoinMarket and its API service in a regtest setup. +To ease development and testing, we provide a Docker setup that runs JoinMarket NG and its API service in a regtest setup. ## Local setup (regtest and UI) @@ -22,7 +22,7 @@ Install dependencies: npm install ``` -Start the JoinMarket HTTP API service in regtest. +Start the JoinMarket NG HTTP API service in regtest. It isn't needed to dig deeper into how it works to use it. However, if you want to find out more about it, see [docker/regtest/readme.md](docker/regtest/readme.md). diff --git a/README.md b/README.md index 6025f179..8a14fd41 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ --- -Jam is a web interface for [JoinMarket](https://github.com/JoinMarket-Org/joinmarket-clientserver/) focusing on user-friendliness and ease-of-use. +Jam is a web interface for [JoinMarket NG](https://github.com/joinmarket-ng/joinmarket-ng) focusing on user-friendliness and ease-of-use. It aims to provide sensible defaults and be easy to use for beginners while still having the features advanced users expect. - 💬 Join our [Matrix room](https://matrix.to/#/%23jam:bitcoin.kyoto) to get help and [contribute](https://joinmarket-webui.github.io/jamdocs/contribute/)! @@ -88,4 +88,4 @@ Want to get your hands dirty with code? Great! [CONTRIBUTING.md](CONTRIBUTING.md This project builds upon the [jm-web-client](https://github.com/JoinMarket-Org/jm-web-client) which was developed by [Shobhitaa](https://github.com/shobhitaa), [Abhishek](https://github.com/abhishek0405), and [waxwing](https://github.com/AdamISZ) himself. Many people contributed over time, some of which are [listed here](https://github.com/joinmarket-webui/jam/graphs/contributors). -Jam and JoinMarket are separate projects. For more information on JoinMarket, see the [JoinMarket GitHub Page](https://github.com/JoinMarket-Org/joinmarket-clientserver). +Jam and JoinMarket NG are separate projects. For more information on JoinMarket NG, see the [JoinMarket NG GitHub Page](https://github.com/joinmarket-ng/joinmarket-ng). diff --git a/docker/regtest/docker-compose-common.yml b/docker/regtest/docker-compose-common.yml index 46490da6..0bd00c63 100644 --- a/docker/regtest/docker-compose-common.yml +++ b/docker/regtest/docker-compose-common.yml @@ -12,10 +12,6 @@ services: jm_rpc_host: bitcoind jm_rpc_port: 43782 jm_directory_nodes: ${JM_ALL_DIRECTORY_NODES:?You must set the directory node addresses in generated env file} - jm_socks5_host: tor - jm_socks5_port: 9050 - jm_tor_control_host: tor - jm_tor_control_port: 9051 jm_minimum_makers: 1 # necessary to do coinjoins with this regtest setup; default is 4 jm_taker_utxo_age: 1 # faster testing of scheduler runs; default is 5 jm_maker_timeout_sec: 30 # easier testing of maker timeouts on regtest (and "Stall Monitor" retries); default is 60 @@ -45,10 +41,6 @@ services: jm_rpc_host: bitcoind jm_rpc_port: 43782 jm_directory_nodes: ${JM_ALL_DIRECTORY_NODES:?You must set the directory node addresses in generated env file} - jm_socks5_host: tor - jm_socks5_port: 9050 - jm_tor_control_host: tor - jm_tor_control_port: 9051 jm_minimum_makers: 1 # necessary to do coinjoins with this regtest setup; default is 4 jm_taker_utxo_age: 1 # faster testing of scheduler runs; default is 5 expose: @@ -67,6 +59,16 @@ services: context: ./dockerfile-deps/joinmarket/webui-standalone-ng dockerfile: Dockerfile restart: unless-stopped + environment: + JMWALLETD_HOST: 0.0.0.0 + BITCOIN__BACKEND_TYPE: descriptor_wallet + BITCOIN__RPC_URL: http://bitcoind:43782 + BITCOIN__RPC_USER: regtest + BITCOIN__RPC_PASSWORD: regtest + LOGGING__LEVEL: DEBUG + NETWORK_CONFIG__NETWORK: testnet + NETWORK_CONFIG__BITCOIN_NETWORK: regtest + NETWORK_CONFIG__DIRECTORY_SERVERS: ${JM_ALL_DIRECTORY_NODES:?You must set the directory node addresses in generated env file} expose: - 80 # nginx - 28183 # jmwalletd api @@ -77,7 +79,6 @@ services: environment: JOINMARKET_DATA_DIR: /root/.joinmarket-ng JMWALLETD_HOST: 0.0.0.0 - JMWALLETD_NO_TLS: ${JMWALLETD_NO_TLS:-false} BITCOIN__BACKEND_TYPE: descriptor_wallet BITCOIN__RPC_URL: http://bitcoind:43782 BITCOIN__RPC_USER: regtest @@ -85,7 +86,8 @@ services: NETWORK_CONFIG__NETWORK: testnet NETWORK_CONFIG__BITCOIN_NETWORK: regtest NETWORK_CONFIG__DIRECTORY_SERVERS: ${JM_ALL_DIRECTORY_NODES:?You must set the directory node addresses in generated env file} - DIRECTORY_NODES: ${JM_ALL_DIRECTORY_NODES:?You must set the directory node addresses in generated env file} + ORDERBOOK_WATCHER__HTTP_HOST: joinmarket_ng_orderbook_watcher + ORDERBOOK_WATCHER__HTTP_PORT: 8000 OBWATCH_URL: http://joinmarket_ng_orderbook_watcher:8000 TOR__SOCKS_HOST: tor TOR__SOCKS_PORT: 9050 @@ -97,7 +99,6 @@ services: expose: - 8000 # obwatch - 28183 # jmwalletd api - - 28283 # jmwalletd websocket healthcheck: test: [ @@ -118,7 +119,6 @@ services: environment: JOINMARKET_DATA_DIR: /root/.joinmarket-ng JMWALLETD_HOST: 0.0.0.0 - JMWALLETD_NO_TLS: ${JMWALLETD_NO_TLS:-false} BITCOIN__BACKEND_TYPE: descriptor_wallet BITCOIN__RPC_URL: http://bitcoind:43782 BITCOIN__RPC_USER: regtest @@ -126,7 +126,8 @@ services: NETWORK_CONFIG__NETWORK: testnet NETWORK_CONFIG__BITCOIN_NETWORK: regtest NETWORK_CONFIG__DIRECTORY_SERVERS: ${JM_ALL_DIRECTORY_NODES:?You must set the directory node addresses in generated env file} - DIRECTORY_NODES: ${JM_ALL_DIRECTORY_NODES:?You must set the directory node addresses in generated env file} + ORDERBOOK_WATCHER__HTTP_HOST: joinmarket_ng_orderbook_watcher + ORDERBOOK_WATCHER__HTTP_PORT: 8000 OBWATCH_URL: http://joinmarket_ng_orderbook_watcher:8000 TOR__SOCKS_HOST: tor TOR__SOCKS_PORT: 9050 @@ -137,7 +138,6 @@ services: expose: - 8000 # obwatch - 28183 # jmwalletd api - - 28283 # jmwalletd websocket healthcheck: test: [ diff --git a/docker/regtest/docker-compose.yml b/docker/regtest/docker-compose.yml index a33c7d8e..9adaeeca 100644 --- a/docker/regtest/docker-compose.yml +++ b/docker/regtest/docker-compose.yml @@ -6,12 +6,10 @@ services: service: joinmarket_ng_native environment: READY_FILE: /root/.regtest-initializer/btc_fully_synched - BITCOIN__RPC_USER: regtest - BITCOIN__RPC_PASSWORD: regtest - BITCOIN__DESCRIPTOR_WALLET_NAME: jm_primary + BITCOIN__RPC_USER: joinmarket + BITCOIN__RPC_PASSWORD: joinmarket ports: - - '28183:28183' # jmwalletd api - - '28283:28283' # jmwalletd websocket + - '28183:28183' # jmwalletd api (exposed for "init setup" routine) volumes: - 'joinmarket_datadir:/root/.joinmarket-ng' - 'initializer_datadir:/root/.regtest-initializer' @@ -32,20 +30,13 @@ services: file: docker-compose-common.yml service: joinmarket_jam_standalone_ng environment: - ENSURE_WALLET: 'true' - BITCOIN__RPC_URL: http://bitcoind:43782 - BITCOIN__RPC_USER: regtest - BITCOIN__RPC_PASSWORD: regtest - BITCOIN__DESCRIPTOR_WALLET_NAME: jm_secondary - LOGGING__LEVEL: DEBUG - NETWORK_CONFIG__NETWORK: testnet - NETWORK_CONFIG__BITCOIN_NETWORK: regtest - NETWORK_CONFIG__DIRECTORY_SERVERS: ${JM_ALL_DIRECTORY_NODES:?You must set the directory node addresses in generated env file} + BITCOIN__RPC_USER: joinmarket2 + BITCOIN__RPC_PASSWORD: joinmarket2 TAKER__MINIMUM_MAKERS: 2 # necessary to do coinjoins with this regtest setup TAKER__MAKER_TIMEOUT_SEC: 30 # easier testing of maker timeouts on regtest ports: - '29080:80' - - '29183:28183' # exposed for "init setup" routine + - '29183:28183' # jmwalletd api (exposed for "init setup" routine) volumes: - 'joinmarket2_datadir:/root/.joinmarket-ng' depends_on: @@ -64,10 +55,11 @@ services: jm_rpc_wallet_file: jm_tertiary jm_rpc_user: joinmarket3 jm_rpc_password: joinmarket3 + CREATE_WALLET_PARAMS_DESCRIPTORS: 'false' ports: - - '30601:62601' # obwatch - - '30183:28183' # jmwalletd api - - '30283:28283' # jmwalletd websocket + - '30601:62601' # obwatch + - '30183:28183' # jmwalletd api (exposed for "init setup" routine) + - '30283:28283' # jmwalletd websocket volumes: - 'joinmarket3_datadir:/root/.joinmarket' - 'initializer_datadir:/root/.regtest-initializer' @@ -93,7 +85,7 @@ services: jm_rpc_cookie_file: /root/.bitcoin_data/regtest/.cookie ports: - '31080:80' - - '31183:28183' # exposed for "init setup" routine + - '31183:28183' # jmwalletd api (exposed for "init setup" routine) volumes: - 'joinmarket4_datadir:/root/.joinmarket' - 'bitcoin_datadir:/root/.bitcoin_data' # mount bitcoind dir to access .cookie file @@ -113,12 +105,8 @@ services: extends: file: docker-compose-common.yml service: joinmarket_ng_peer - environment: - BITCOIN__RPC_USER: regtest - BITCOIN__RPC_PASSWORD: regtest - BITCOIN__DESCRIPTOR_WALLET_NAME: jm_quinary ports: - - '32183:28183' + - '32183:28183' # jmwalletd api (exposed for "init setup" routine) volumes: - 'joinmarket5_datadir:/root/.joinmarket-ng' - 'tor_datadir:/var/lib/tor:ro' @@ -132,8 +120,8 @@ services: tor: condition: service_healthy - joinmarket_directory_node: - container_name: jm_regtest_joinmarket_directory_node + joinmarket_clientserver_directory_node: + container_name: jm_regtest_joinmarket_clientserver_directory_node build: context: ./dockerfile-deps/joinmarket/directory_node dockerfile: Dockerfile @@ -141,15 +129,10 @@ services: environment: jm_hidden_service_dir: \/root\/.joinmarket\/hidden_service_dir jm_directory_nodes: ${JM_REF_DIRECTORY_NODES:?You must set the reference directory node address in generated env file} - jm_socks5_host: localhost - jm_socks5_port: 9050 - jm_tor_control_host: localhost - jm_tor_control_port: 9051 - jm_daemon_host: 0.0.0.0 jm_blockchain_source: regtest jm_network: testnet volumes: - - 'joinmarket_directory_node_datadir:/root/.joinmarket' + - 'joinmarket_clientserver_directory_node_datadir:/root/.joinmarket' - './out/hidden_service_dir:/root/.joinmarket/hidden_service_dir:z' healthcheck: test: ['CMD', 'supervisorctl', 'status'] @@ -161,9 +144,6 @@ services: depends_on: tor: condition: service_healthy - networks: - default: - ipv4_address: 172.30.0.20 joinmarket_ng_orderbook_watcher: container_name: jm_regtest_joinmarket_ng_orderbook_watcher @@ -176,6 +156,8 @@ services: TOR__SOCKS_HOST: tor TOR__SOCKS_PORT: 9050 LOGGING__LEVEL: DEBUG + ORDERBOOK_WATCHER__HTTP_HOST: 0.0.0.0 + ORDERBOOK_WATCHER__HTTP_PORT: 8000 expose: - 8000 ports: @@ -243,11 +225,10 @@ services: [ 'sh', '-c', - 'mkdir -p /var/lib/tor/ref-directory /var/lib/tor/ng-directory && cp -f /source/ref/* /var/lib/tor/ref-directory/ && cp -f /source/ng/* /var/lib/tor/ng-directory/ && chown -R 1000:1000 /var/lib/tor && chmod 700 /var/lib/tor/ref-directory /var/lib/tor/ng-directory && chmod 600 /var/lib/tor/ref-directory/* /var/lib/tor/ng-directory/*', + 'mkdir -p /var/lib/tor/ng-directory && cp -f /source/ng/* /var/lib/tor/ng-directory/ && chown -R 1000:1000 /var/lib/tor && chmod 700 /var/lib/tor/ng-directory && chmod 600 /var/lib/tor/ng-directory/*', ] volumes: - 'tor_datadir:/var/lib/tor' - - './out/hidden_service_dir:/source/ref:ro,z' - './out/ng_directory_hidden_service:/source/ng:ro,z' irc: @@ -262,7 +243,7 @@ services: bitcoind: container_name: jm_regtest_bitcoind restart: unless-stopped - image: polarlightning/bitcoind:28.0@sha256:ff435d758c89f01823924e057d5d5e4206d350ae69dff5e0dc1bbc9c6a36d010 + image: polarlightning/bitcoind:29.0@sha256:4521294ac25fdbf1492a8cc5ea987463a5f244b82546d29bbe8451db39f36f3a command: # rpcauth (user=regtest; password=regtest) # rpcauth (user=joinmarket; password=joinmarket) @@ -388,7 +369,7 @@ volumes: joinmarket3_datadir: joinmarket4_datadir: joinmarket5_datadir: - joinmarket_directory_node_datadir: + joinmarket_clientserver_directory_node_datadir: tor_datadir: irc_datadir: diff --git a/docker/regtest/dockerfile-deps/joinmarket/directory_node/README.md b/docker/regtest/dockerfile-deps/joinmarket/directory_node/README.md index 0f790880..c1c3b127 100644 --- a/docker/regtest/dockerfile-deps/joinmarket/directory_node/README.md +++ b/docker/regtest/dockerfile-deps/joinmarket/directory_node/README.md @@ -9,7 +9,7 @@ and provide the correct onion hostname via `directory_nodes` yourself! e.g. in a docker-compose setup: ```yml - joinmarket_directory_node: + joinmarket_clientserver_directory_node: [...] environment: jm_hidden_service_dir: \/root\/.joinmarket\/hidden_service_dir diff --git a/docker/regtest/dockerfile-deps/joinmarket/latest/jam-entrypoint.sh b/docker/regtest/dockerfile-deps/joinmarket/latest/jam-entrypoint.sh index b3e0a4ba..c760ccb8 100644 --- a/docker/regtest/dockerfile-deps/joinmarket/latest/jam-entrypoint.sh +++ b/docker/regtest/dockerfile-deps/joinmarket/latest/jam-entrypoint.sh @@ -93,7 +93,7 @@ if [ "${ENSURE_WALLET}" = "true" ]; then \"method\":\"createwallet\",\ \"params\":{\ \"wallet_name\":\"${wallet_name}\",\ - \"descriptors\":false,\ + \"descriptors\":$( [[ "${CREATE_WALLET_PARAMS_DESCRIPTORS,,}" == "false" ]] && echo false || echo true ),\ \"load_on_startup\":true\ }\ }" diff --git a/docker/regtest/dockerfile-deps/joinmarket/latest/torrc b/docker/regtest/dockerfile-deps/joinmarket/latest/torrc index acca9872..ece59b60 100644 --- a/docker/regtest/dockerfile-deps/joinmarket/latest/torrc +++ b/docker/regtest/dockerfile-deps/joinmarket/latest/torrc @@ -1,7 +1,7 @@ # Default JoinMarket Tor configuration # taken from v0.9.6 on 2022-07-18: # https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/v0.9.6/install.sh#L388-L393 -Log warn stderr +Log notice stdout SOCKSPort 9050 IsolateDestAddr IsolateDestPort ControlPort 9051 CookieAuthentication 1 diff --git a/docker/regtest/init-setup.sh b/docker/regtest/init-setup.sh index a5093503..b1f7e03e 100755 --- a/docker/regtest/init-setup.sh +++ b/docker/regtest/init-setup.sh @@ -16,7 +16,9 @@ set -Eeuo pipefail script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) # fund wallet in primary JoinMarket NG container -. "$script_dir/fund-wallet.sh" --container jm_regtest_joinmarket --unmatured --blocks 3 +. "$script_dir/fund-wallet.sh" --container jm_regtest_joinmarket --mixdepth 0 --unmatured --blocks 1 +. "$script_dir/fund-wallet.sh" --container jm_regtest_joinmarket --mixdepth 1 --unmatured --blocks 2 # outputs should be flagged as "reused" +. "$script_dir/fund-wallet.sh" --container jm_regtest_joinmarket --mixdepth 4 --unmatured --blocks 1 # fund wallet in ng standalone secondary container . "$script_dir/fund-wallet.sh" --container jm_regtest_joinmarket2 --unmatured --blocks 50 @@ -35,10 +37,14 @@ script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P) dummy_wallet_address1='bcrt1q6rz28mcfaxtmd6v789l9rrlrusdprr9pz3cppk' # 1st address of jar A (m/84'/1'/0'/0/0) dummy_wallet_address2='bcrt1qt5yxk3xzrx66q9wd5sdyynklqynqcyf7uh74j3' # 8th address of jar C (m/84'/1'/2'/0/7) dummy_wallet_address3='bcrt1qn8804dw5fahuc5cwqteuq5j4xlhk2cnkq7a8kw' # 21st change address of jar E (m/84'/1'/4'/1/21) -# make block rewards spendable: 100 + 5 (default of `taker_utxo_age`) + 1 = 106 +dummy_wallet_address_fb0='bcrt1q7war3lusq3ez633rzzfkp75unfcgrqqcu80sgp0ellhrdvkzxh5swspygk' # 1st fb address (m/84'/1'/0'/2/0) +dummy_wallet_address_fb959='bcrt1q6rlcknptmqhf4w20928tpz6px4y70960gyupd029c7vx7kdjwt0q46nk9c' # 960th fb address (m/84'/1'/0'/2/959) +# make block rewards spendable: 100 + 5 (default of `taker_utxo_age`) + 1 = at least 106 blocks . "$script_dir/mine-block.sh" 2 "$dummy_wallet_address1" &>/dev/null . "$script_dir/mine-block.sh" 2 "$dummy_wallet_address2" &>/dev/null . "$script_dir/mine-block.sh" 2 "$dummy_wallet_address3" &>/dev/null +. "$script_dir/mine-block.sh" 1 "$dummy_wallet_address_fb0" &>/dev/null +. "$script_dir/mine-block.sh" 1 "$dummy_wallet_address_fb959" &>/dev/null . "$script_dir/mine-block.sh" 100 &>/dev/null start_maker() { diff --git a/docker/regtest/readme.md b/docker/regtest/readme.md index de09968d..318a7d21 100644 --- a/docker/regtest/readme.md +++ b/docker/regtest/readme.md @@ -120,7 +120,7 @@ Port mapping overview: | Container | Ports | | --------------------------------------- | ---------------------------------------------- | -| `joinmarket` (ng native) | `28183` (API), `28283` (WS), `62601` (obwatch) | +| `joinmarket` (ng native) | `28183` (API) | | `joinmarket2` (ng standalone) | `29080` (UI), `29183` (API) | | `joinmarket3` (clientserver native) | `30183` (API), `30283` (WS), `30601` (obwatch) | | `joinmarket4` (clientserver standalone) | `31080` (UI, basic auth), `31183` (API) | diff --git a/docker/regtest/tor/conf/torrc b/docker/regtest/tor/conf/torrc index 6e7ef81a..7a7d630c 100644 --- a/docker/regtest/tor/conf/torrc +++ b/docker/regtest/tor/conf/torrc @@ -9,11 +9,6 @@ ControlPort 0.0.0.0:9051 CookieAuthentication 1 CookieAuthFile /var/lib/tor/control_auth_cookie -# Hidden service for JoinMarket reference directory node -HiddenServiceDir /var/lib/tor/ref-directory -HiddenServicePort 5222 172.30.0.20:8080 -HiddenServiceVersion 3 - # Hidden service for JoinMarket NG directory node HiddenServiceDir /var/lib/tor/ng-directory HiddenServicePort 5222 172.30.0.21:5223 diff --git a/docs/developing.md b/docs/developing.md index 183490e5..1355b4c7 100644 --- a/docs/developing.md +++ b/docs/developing.md @@ -6,9 +6,9 @@ A place to collect useful information for developers that doesn't really fit els ## JoinMarket Development Environment -For a complete development environment you need a local JoinMarket instance that the web UI can interact with. We provide a regtest environment that should give you everything needed to get started developing with JoinMarket. You can find details here: [docker/regtest/readme.md](../docker/regtest/readme.md). +For a complete development environment you need a local JoinMarket NG instance that the web UI can interact with. We provide a regtest environment that should give you everything needed to get started developing with JoinMarket NG. You can find details here: [docker/regtest/readme.md](../docker/regtest/readme.md). -## Running Jam Against JoinMarket-NG +## Running Jam Against JoinMarket NG Jam v2 can talk directly to a separately running `jmwalletd` / orderbook watcher from `joinmarket-ng`. You do not need to run the Jam regtest compose or the reference implementation for this workflow. @@ -26,7 +26,7 @@ If your separately running joinmarket-ng services use different ports, you can o JAM_BACKEND=joinmarket-ng \ JMWALLETD_API_PORT=28183 \ JMWALLETD_WEBSOCKET_PORT=28283 \ -JMOBWATCH_PORT=8080 \ +JMOBWATCH_PORT=8000 \ npm run dev ``` @@ -86,17 +86,17 @@ By default, the hook will be installed automatically as an [NPM postinstall scri If you're having issues with Husky not using the correct `$PATH`, you may need to setup a `~/.huskyrc` which will let you set up your path before the hook is run. See [here](https://typicode.github.io/husky/#/?id=command-not-found) for more info. -## Running the Web UI Locally and Connecting to a Remote JoinMarket Instance +## Running the Web UI Locally and Connecting to a Remote JoinMarket NG Instance -These instructions assume you want to run the web UI locally and connect it to a JoinMarket instance on your RaspiBlitz. +These instructions assume you want to run the web UI locally and connect it to a JoinMarket NG instance on your RaspiBlitz. The process should be similar for other setups. -If you run the Web UI and JoinMarket on the same system, simply skip the SSH tunnel step. +If you run the Web UI and JoinMarket NG on the same system, simply skip the SSH tunnel step. -### 🚨 Prerequisite: JoinMarket +### 🚨 Prerequisite: JoinMarket NG -To run the web UI locally you need to connect it to a running JoinMarket instance. +To run the web UI locally you need to connect it to a running JoinMarket NG instance. -#### 1. Install JoinMarket +#### 1. Install JoinMarket NG Install [JoininBox](https://github.com/openoms/joininbox) on your [RaspiBlitz](https://github.com/rootzoll/raspiblitz): @@ -104,38 +104,38 @@ Install [JoininBox](https://github.com/openoms/joininbox) on your [RaspiBlitz](h Services > j [BTC JoinMarket+JoininBox menu] ``` -Or follow the JoinMarket [installation guide](https://github.com/JoinMarket-Org/joinmarket-clientserver#quickstart---recommended-installation-method-linux-and-macos-only) if you're on another system. +Or follow the JoinMarket NG [installation guide](https://joinmarket-ng.github.io/joinmarket-ng/install/) if you're on another system. -### 🚨 Prerequisite: JoinMarket API Service +### 🚨 Prerequisite: JoinMarket NG API Service -This app makes use of the JoinMarket RPC API. For this, you will need JoinMarket version 0.9.3 or higher. If needed you can upgrade JoinMarket to the latest commit via the JoininBox menu on your RaspiBlitz: Type `jm` in the command line and select `UPDATE > ADVANCED > JMCOMMIT`. This will install the latest development version from JoinMarket's master branch. +This app makes use of the JoinMarket NG API. #### 2. SSL Certificate -As the joinmarket user on your RaspiBlitz, generate a self-signed certificate for the JoinMarket API Service as described [here](https://linuxize.com/post/creating-a-self-signed-ssl-certificate/), and put the certificate and the key in the `~/.joinmarket/ssl/` directory. +As the joinmarket user on your RaspiBlitz, generate a self-signed certificate for the JoinMarket NG API Service as described [here](https://linuxize.com/post/creating-a-self-signed-ssl-certificate/), and put the certificate and the key in the `~/.joinmarket-ng/ssl/` directory. _Hint:_ To login as the JoinMarket user, you can ssh into your RaspiBlitz, type `jm`, and exit the JoininBox menu. Create the SSL directory: ```bash -(jmvenv) joinmarket@raspberrypi:~ $ mkdir ~/.joinmarket/ssl/ +(jmvenv) joinmarket@raspberrypi:~ $ mkdir ~/.joinmarket-ng/ssl/ ``` Generate the certificate and associated key: ```bash -openssl req -newkey rsa:4096 -x509 -sha256 -days 3650 -nodes -out ~/.joinmarket/ssl/cert.pem -keyout ~/.joinmarket/ssl/key.pem +openssl req -newkey rsa:4096 -x509 -sha256 -days 3650 -nodes -out ~/.joinmarket-ng/ssl/cert.pem -keyout ~/.joinmarket-ng/ssl/key.pem ``` _Hint:_ You don't have to enter anything meaningful, you can just hit the return key a couple of times. #### 3. API Service -Start the JoinMarket [API service](https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/docs/JSON-RPC-API-using-jmwalletd.md): +Start the JoinMarket NG [API service](https://joinmarket-ng.github.io/joinmarket-ng/README-jmwalletd/): ```bash -(jmvenv) joinmarket@raspberrypi:~/joinmarket-clientserver/scripts $ python jmwalletd.py +(jmvenv) joinmarket@raspberrypi:~/joinmarket-ng $ jmwalletd serve ``` You should see the following: diff --git a/eslint.config.js b/eslint.config.js index d96a277e..24065352 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -125,6 +125,12 @@ export default defineConfig( }, }, ], + 'unicorn/number-literal-case': [ + 'error', + { + hexadecimalValue: 'lowercase', + }, + ], 'unicorn/switch-case-braces': ['off'], // TODO: enable 'unicorn/no-null': ['off'], // TODO: enable diff --git a/index.html b/index.html index 98330135..46e28189 100644 --- a/index.html +++ b/index.html @@ -2,11 +2,12 @@ + - + Jam for JoinMarket diff --git a/package-lock.json b/package-lock.json index 555a064a..e386f3e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,164 +1,167 @@ { "name": "jam", - "version": "2.0.0-beta.0", + "version": "2.0.0-beta.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jam", - "version": "2.0.0-beta.0", + "version": "2.0.0-beta.2", "license": "MIT", "dependencies": { - "@hookform/resolvers": "5.4.0", - "@joinmarket-webui/joinmarket-api-ts": "0.3.0", - "@noble/hashes": "2.2.0", - "@radix-ui/react-accordion": "1.2.14", - "@radix-ui/react-avatar": "1.2.0", - "@radix-ui/react-checkbox": "1.3.5", - "@radix-ui/react-dialog": "1.1.17", - "@radix-ui/react-dropdown-menu": "2.1.18", - "@radix-ui/react-label": "2.1.10", - "@radix-ui/react-popover": "1.1.17", - "@radix-ui/react-radio-group": "1.4.1", - "@radix-ui/react-select": "2.3.1", - "@radix-ui/react-separator": "1.1.10", - "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-switch": "1.3.1", - "@radix-ui/react-tabs": "1.1.15", - "@radix-ui/react-tooltip": "1.2.10", + "@hookform/resolvers": "5.7.1", + "@joinmarket-webui/joinmarket-ng-api-ts": "1.0.0", + "@noble/hashes": "2.3.0", + "@radix-ui/react-accordion": "1.2.20", + "@radix-ui/react-avatar": "1.2.6", + "@radix-ui/react-checkbox": "1.3.11", + "@radix-ui/react-dialog": "1.1.23", + "@radix-ui/react-dropdown-menu": "2.1.24", + "@radix-ui/react-label": "2.1.15", + "@radix-ui/react-popover": "1.1.23", + "@radix-ui/react-radio-group": "1.4.7", + "@radix-ui/react-select": "2.3.7", + "@radix-ui/react-separator": "1.1.15", + "@radix-ui/react-slider": "1.4.7", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-switch": "1.3.7", + "@radix-ui/react-tabs": "1.1.21", + "@radix-ui/react-tooltip": "1.2.16", "@scure/bip32": "2.2.0", "@scure/bip39": "2.2.0", - "@tailwindcss/vite": "4.3.1", + "@tailwindcss/vite": "4.3.3", "@tanstack/match-sorter-utils": "8.19.4", - "@tanstack/react-query": "5.101.0", + "@tanstack/react-query": "5.101.4", "@tanstack/react-table": "8.21.3", "bitcoin-address-validation": "3.0.0", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "html5-qrcode": "2.3.8", "i18next-browser-languagedetector": "8.2.1", - "lucide-react": "1.20.0", + "lucide-react": "1.30.0", "next-themes": "0.4.6", "qrcode": "1.5.4", - "radix-ui": "1.6.0", - "react": "19.2.7", - "react-dom": "19.2.7", - "react-hook-form": "7.79.0", - "react-i18next": "17.0.8", - "react-router-dom": "7.18.0", + "radix-ui": "1.6.7", + "react": "19.2.8", + "react-dom": "19.2.8", + "react-hook-form": "7.84.0", + "react-i18next": "17.0.11", + "react-router-dom": "7.18.2", "react-use-websocket": "4.13.0", "sonner": "2.0.7", "tailwind-merge": "3.6.0", - "tailwindcss": "4.3.1", + "tailwindcss": "4.3.3", "tw-animate-css": "1.4.0", "yup": "1.7.1", "zustand": "5.0.14" }, "devDependencies": { - "@chromatic-com/storybook": "5.2.1", - "@eslint/js": "9.39.4", - "@playwright/test": "1.61.0", - "@storybook/addon-a11y": "10.4.6", - "@storybook/addon-docs": "10.4.6", - "@storybook/addon-vitest": "10.4.6", - "@storybook/react-vite": "10.4.6", - "@tanstack/eslint-plugin-query": "5.101.0", - "@testing-library/jest-dom": "6.9.1", + "@chromatic-com/storybook": "5.3.0", + "@eslint/js": "9.39.5", + "@playwright/test": "1.62.0", + "@storybook/addon-a11y": "10.5.7", + "@storybook/addon-docs": "10.5.7", + "@storybook/addon-vitest": "10.5.7", + "@storybook/react-vite": "10.5.7", + "@tanstack/eslint-plugin-query": "5.101.4", + "@testing-library/jest-dom": "7.0.0", "@testing-library/react": "16.3.2", - "@testing-library/user-event": "14.6.1", + "@testing-library/user-event": "14.6.3", "@trivago/prettier-plugin-sort-imports": "6.0.2", - "@types/node": "24.12.4", + "@types/node": "26.1.2", "@types/qrcode": "1.5.6", - "@types/react": "19.2.17", - "@types/react-dom": "19.2.3", - "@vitejs/plugin-react": "6.0.2", - "@vitest/browser": "4.1.9", - "@vitest/browser-playwright": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "conventional-changelog": "7.2.0", - "eslint": "9.39.4", + "@types/react": "19.2.18", + "@types/react-dom": "19.2.4", + "@vitejs/plugin-react": "6.0.5", + "@vitest/browser": "4.1.10", + "@vitest/browser-playwright": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "conventional-changelog": "8.1.1", + "eslint": "9.39.5", "eslint-plugin-compat": "7.0.2", "eslint-plugin-react-hooks": "7.1.1", "eslint-plugin-react-refresh": "0.5.3", - "eslint-plugin-storybook": "10.4.6", - "eslint-plugin-unicorn": "64.0.0", - "globals": "17.6.0", + "eslint-plugin-storybook": "10.5.7", + "eslint-plugin-unicorn": "65.0.1", + "globals": "17.9.0", "husky": "9.1.7", - "jsdom": "29.1.1", - "lint-staged": "17.0.7", - "msw": "2.14.6", - "msw-storybook-addon": "2.0.7", - "prettier": "3.8.4", - "prettier-plugin-tailwindcss": "0.8.0", - "storybook": "10.4.6", + "jsdom": "30.0.1", + "lint-staged": "17.3.0", + "msw": "2.15.0", + "msw-storybook-addon": "3.0.0", + "prettier": "3.9.6", + "prettier-plugin-tailwindcss": "0.8.1", + "storybook": "10.5.7", "typescript": "6.0.3", - "typescript-eslint": "8.61.1", - "vite": "8.0.16", - "vitest": "4.1.9" + "typescript-eslint": "8.66.0", + "vite": "8.2.1", + "vitest": "4.1.10" }, "engines": { - "node": ">=24.11.1", - "npm": ">=11.6.2" + "node": ">=26.3.0", + "npm": ">=11.16.0" } }, "node_modules/@adobe/css-tools": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", - "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", "dev": true, "license": "MIT" }, "node_modules/@asamuzakjp/css-color": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", - "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-6.0.5.tgz", + "integrity": "sha512-mbhpPMmnw/kwW19aRNmSUl1QzLbdGo1SCuE49BT98MNwqF6zaHb3o2owssFc/PEO/4t2UjqtCNwocuDtJornzA==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/generational-cache": "^1.0.1", - "@csstools/css-calc": "^3.2.0", - "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-calc": "^3.2.1", + "@csstools/css-color-parser": "^4.1.9", "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.5.2" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": "^22.13.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", - "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-8.3.0.tgz", + "integrity": "sha512-UJLfKXBhrc8i1vH2eJXuYQMwlsLKWFw3O+CPqXSuVEiikeAim3UgrfWX0k4tA/X8cRFM8iZ7OaqBokFGbYusdg==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/generational-cache": "^1.0.1", - "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", - "is-potential-custom-element-name": "^1.0.1" + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.5.2" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": "^22.13.0 || >=24.0.0" } }, - "node_modules/@asamuzakjp/generational-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", - "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": "20 || >=22" } }, - "node_modules/@asamuzakjp/nwsapi": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/code-frame": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", @@ -372,9 +375,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -459,14 +462,14 @@ } }, "node_modules/@chromatic-com/storybook": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-5.2.1.tgz", - "integrity": "sha512-z6I7NJk/0VngA64y5TNYaB4Hc2X8+90n4op6lBt9PvWk5TmIlFLDqdX33rlrwbNRkkYijVgA/wO04rVYXi5Mlg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-5.3.0.tgz", + "integrity": "sha512-gMAfVYJnF/tjdY8C3Q54KYz/low2NFjPIZMpCqSFuMUiTJ2DNWCTb7mFhuuzZ1iduWFDhewlJyilFmvKcEDYRA==", "dev": true, "license": "MIT", "dependencies": { "@neoconfetti/react": "^1.0.0", - "chromatic": "16.10.0", + "chromatic": "^18.0.1", "jsonfile": "^6.1.0", "strip-ansi": "^7.1.0" }, @@ -479,22 +482,22 @@ } }, "node_modules/@conventional-changelog/git-client": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.7.0.tgz", - "integrity": "sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-3.1.0.tgz", + "integrity": "sha512-Tqa/gHco2WJWa740NRjOrfKVvzIqxkZpecb8bemaQ8sKM5PXb1UK4uTyTb/1wIqNuOVaDOFxyBdhTIQZn6gdjQ==", "dev": true, "license": "MIT", "dependencies": { - "@simple-libs/child-process-utils": "^1.0.0", - "@simple-libs/stream-utils": "^1.2.0", + "@simple-libs/child-process-utils": "^2.0.0", + "@simple-libs/stream-utils": "^2.0.0", "semver": "^7.5.2" }, "engines": { - "node": ">=18" + "node": ">=22" }, "peerDependencies": { - "conventional-commits-filter": "^5.0.0", - "conventional-commits-parser": "^6.4.0" + "conventional-commits-filter": "^6.0.1", + "conventional-commits-parser": "^7.0.1" }, "peerDependenciesMeta": { "conventional-commits-filter": { @@ -505,10 +508,20 @@ } } }, + "node_modules/@conventional-changelog/template": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.2.1.tgz", + "integrity": "sha512-TzlTVpKPjaqW6qOYjQcYUDuGsLCNsvFHVBXkYGTAnf5V37jCWrE5haKNXzz0WZUtVHjrpV76L1buANjwXMfT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, "node_modules/@csstools/color-helpers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", - "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz", + "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==", "dev": true, "funding": [ { @@ -526,9 +539,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", - "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", "dev": true, "funding": [ { @@ -550,9 +563,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz", - "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz", + "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", "dev": true, "funding": [ { @@ -566,8 +579,8 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^6.0.2", - "@csstools/css-calc": "^3.2.1" + "@csstools/color-helpers": "^6.1.0", + "@csstools/css-calc": "^3.3.0" }, "engines": { "node": ">=20.19.0" @@ -601,9 +614,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz", - "integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", "dev": true, "funding": [ { @@ -646,30 +659,20 @@ } }, "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/core/node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", "license": "MIT", "optional": true, "dependencies": { + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "license": "MIT", "optional": true, "dependencies": { @@ -1147,9 +1150,9 @@ } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1197,9 +1200,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", "dependencies": { @@ -1209,7 +1212,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -1220,10 +1223,27 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1244,6 +1264,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -1258,9 +1285,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", "engines": { @@ -1295,9 +1322,9 @@ } }, "node_modules/@exodus/bytes": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", - "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", "dev": true, "license": "MIT", "engines": { @@ -1313,31 +1340,31 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", - "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.11" + "@floating-ui/utils": "^0.2.12" } }, "node_modules/@floating-ui/dom": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", - "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", - "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.7.6" + "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -1345,21 +1372,119 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", - "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", "license": "MIT" }, "node_modules/@hookform/resolvers": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.4.0.tgz", - "integrity": "sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.7.1.tgz", + "integrity": "sha512-8wS/P4UDr5sQDe4nFaV51TVyfDPrWgNIXweqG0Bs9Z5LSuzKLb+RQNPvkN2oHM5SRrJyWrVH/F+LOUcFjUyvwQ==", "license": "MIT", "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { - "react-hook-form": "^7.55.0" + "@sinclair/typebox": ">=0.25.24", + "@standard-schema/spec": "^1.0.0", + "@typeschema/main": ">=0.13.7", + "@vinejs/vine": "^2.0.0 || ^3.0.0 || ^4.0.0", + "ajv": "^8.12.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "arktype": "^2.0.0", + "ata-validator": "^1.2.0", + "class-transformer": ">=0.4.0", + "class-validator": ">=0.12.0", + "computed-types": "^1.0.0", + "effect": "^3.10.3", + "fluentvalidation-ts": "^3.0.0", + "fp-ts": "^2.7.0", + "io-ts": "^2.0.0", + "joi": "^17.0.0", + "nope-validator": ">=0.12.0", + "react-hook-form": "^7.55.0", + "superstruct": ">=0.12.0", + "typanion": "^3.3.2", + "valibot": ">=0.31.0 || ^1.0.0-beta.4 || ^1.0.0-rc", + "vest": ">=3.0.0", + "yup": "^1.0.0", + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@sinclair/typebox": { + "optional": true + }, + "@standard-schema/spec": { + "optional": true + }, + "@typeschema/main": { + "optional": true + }, + "@vinejs/vine": { + "optional": true + }, + "ajv": { + "optional": true + }, + "ajv-errors": { + "optional": true + }, + "ajv-formats": { + "optional": true + }, + "arktype": { + "optional": true + }, + "ata-validator": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + }, + "computed-types": { + "optional": true + }, + "effect": { + "optional": true + }, + "fluentvalidation-ts": { + "optional": true + }, + "fp-ts": { + "optional": true + }, + "io-ts": { + "optional": true + }, + "joi": { + "optional": true + }, + "nope-validator": { + "optional": true + }, + "superstruct": { + "optional": true + }, + "typanion": { + "optional": true + }, + "valibot": { + "optional": true + }, + "vest": { + "optional": true + }, + "yup": { + "optional": true + }, + "zod": { + "optional": true + } } }, "node_modules/@humanfs/core": { @@ -1515,13 +1640,18 @@ } } }, - "node_modules/@joinmarket-webui/joinmarket-api-ts": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@joinmarket-webui/joinmarket-api-ts/-/joinmarket-api-ts-0.3.0.tgz", - "integrity": "sha512-Ta7C+jTIHz8zt7kzrdVPH7g0AXUQj7G15VWxGCloYjlTyQofmGwm4cCMXuJYVuybp5qbxPLQilgeyqkY4gJZIQ==", + "node_modules/@joinmarket-webui/joinmarket-ng-api-ts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@joinmarket-webui/joinmarket-ng-api-ts/-/joinmarket-ng-api-ts-1.0.0.tgz", + "integrity": "sha512-/6ykwDbQiSgENLv3il/mqH+M7qIY8dse1Jj8ZkUMbwBKjaO9Knis18CSXL+xrorkJYnXQZ5omNAxrLMMQgjREQ==", "license": "MIT", - "optionalDependencies": { - "@tanstack/react-query": "5.90.9" + "peerDependencies": { + "@tanstack/react-query": "$@tanstack/react-query" + }, + "peerDependenciesMeta": { + "@tanstack/react-query": { + "optional": true + } } }, "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { @@ -1640,13 +1770,13 @@ "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -1679,7 +1809,7 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@noble/hashes": { + "node_modules/@noble/curves/node_modules/@noble/hashes": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", @@ -1691,6 +1821,18 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@noble/hashes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.3.0.tgz", + "integrity": "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@open-draft/deferred-promise": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz", @@ -1843,6 +1985,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1860,6 +2005,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1877,6 +2025,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1894,6 +2045,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1911,6 +2065,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1928,6 +2085,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1945,6 +2105,9 @@ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1962,6 +2125,9 @@ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2007,29 +2173,6 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { "version": "0.127.0", "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.127.0.tgz", @@ -2092,9 +2235,9 @@ } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.24.2.tgz", + "integrity": "sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==", "cpu": [ "arm" ], @@ -2106,9 +2249,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.24.2.tgz", + "integrity": "sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==", "cpu": [ "arm64" ], @@ -2120,9 +2263,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.24.2.tgz", + "integrity": "sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==", "cpu": [ "arm64" ], @@ -2134,9 +2277,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.24.2.tgz", + "integrity": "sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==", "cpu": [ "x64" ], @@ -2148,9 +2291,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.24.2.tgz", + "integrity": "sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==", "cpu": [ "x64" ], @@ -2162,9 +2305,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.24.2.tgz", + "integrity": "sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==", "cpu": [ "arm" ], @@ -2176,9 +2319,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.24.2.tgz", + "integrity": "sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==", "cpu": [ "arm" ], @@ -2190,13 +2333,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.24.2.tgz", + "integrity": "sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2204,13 +2350,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.24.2.tgz", + "integrity": "sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2218,13 +2367,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.24.2.tgz", + "integrity": "sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2232,13 +2384,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.24.2.tgz", + "integrity": "sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2246,13 +2401,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.24.2.tgz", + "integrity": "sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2260,13 +2418,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.24.2.tgz", + "integrity": "sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2274,13 +2435,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.24.2.tgz", + "integrity": "sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2288,13 +2452,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.24.2.tgz", + "integrity": "sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2302,9 +2469,9 @@ ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.24.2.tgz", + "integrity": "sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==", "cpu": [ "arm64" ], @@ -2316,9 +2483,9 @@ ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.24.2.tgz", + "integrity": "sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==", "cpu": [ "wasm32" ], @@ -2326,30 +2493,41 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.2", + "@emnapi/runtime": "1.11.2", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -2358,9 +2536,9 @@ } }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.24.2.tgz", + "integrity": "sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==", "cpu": [ "arm64" ], @@ -2372,9 +2550,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.24.2.tgz", + "integrity": "sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==", "cpu": [ "x64" ], @@ -2386,19 +2564,19 @@ ] }, "node_modules/@playwright/test": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.0.tgz", - "integrity": "sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==", + "version": "1.62.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.0.tgz", + "integrity": "sha512-9zOJ6ZQRAena31MpOH9VSzIz8Ou3YJ/wtY/eQm5T2uhfhG7/U3COrMS8xOtUrZrp9OgdmzEnIYODye3nY1VqzA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.61.0" + "playwright": "1.62.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/@polka/url": { @@ -2409,24 +2587,24 @@ "license": "MIT" }, "node_modules/@radix-ui/number": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.2.tgz", - "integrity": "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz", + "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==", "license": "MIT" }, "node_modules/@radix-ui/primitive": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", - "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", + "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", "license": "MIT" }, "node_modules/@radix-ui/react-accessible-icon": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.10.tgz", - "integrity": "sha512-TraSwZUqTcVbiDV2/RXzAXC7aeVVXchq0daPFZE7zAxYFaMzjOUggLOfQH9KFLgRizuwVKZO/crveV1eeO3/ZQ==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.15.tgz", + "integrity": "sha512-WTQwcAvQf5sOcuUyi90lKPbhwcvQ+j55cjrSmeaN+L2vKU3DooOvlKw2MDeiJ5IkV5N905KW0/fGojKOBhD11A==", "license": "MIT", "dependencies": { - "@radix-ui/react-visually-hidden": "1.2.6" + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -2444,20 +2622,20 @@ } }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.14.tgz", - "integrity": "sha512-iE8YB9nmTBH8zd73ofBISZ8JCzgMoMkATJr7qDwa6u5F1+7mTM81V6fa71jgZ65rpjVpecDf1vSnwIFP9Ly1zw==", + "version": "1.2.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.20.tgz", + "integrity": "sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collapsible": "1.1.14", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collapsible": "1.1.20", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -2475,16 +2653,16 @@ } }, "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.17.tgz", - "integrity": "sha512-563ygGeyWPrxyVCNp7OV4rE2aIXhFPknpFyo4wbDlcyMMPZ6ySh+zC5WTvY0ZFLgPTg/QB6tA8PyDQyJ2b4cPg==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.23.tgz", + "integrity": "sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dialog": "1.1.17", - "@radix-ui/react-primitive": "2.1.6" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dialog": "1.1.23", + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -2502,12 +2680,12 @@ } }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.10.tgz", - "integrity": "sha512-j2VTDz1vgCsmuG0k5lBfOcM8n5JPFqZBcMryasFjHYMhwxYL5SRUV5lMSUpRdNtw3D/Sv8pzJtrlAgkssYSsQQ==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", + "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.6" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -2525,12 +2703,12 @@ } }, "node_modules/@radix-ui/react-aspect-ratio": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.10.tgz", - "integrity": "sha512-kbI7NrqhDeuytYrq7JjAsoXczvL8wgj2tc1MyaYWm+50bMKHCHQtVWCryslx4cCpmCTTkBcwQckE4CmmGV2haQ==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.15.tgz", + "integrity": "sha512-fy+dyVR+90nelK8rqIznFlxzx7uPcGbhxH8Nfr2bHb4UfSe+e3hklOC0luK0hDwVwnRX7xTRySpsrQVeW+/oNQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.6" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -2548,16 +2726,17 @@ } }, "node_modules/@radix-ui/react-avatar": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.0.tgz", - "integrity": "sha512-am/CwltXtmtdtP+5FbYblYDnMa/zuKcMJP1i3/SJMDXXfj2mG+BTqLH2wucqeyyiQMursUtg/5cK+Nh2pCaSOA==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.6.tgz", + "integrity": "sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-is-hydrated": "0.1.1", - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2575,19 +2754,18 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.5.tgz", - "integrity": "sha512-pREzrmNnVwGvYaBoM64huTRK7B3lrTRuwj8A9nwhPiEtMb+yudiWh6zWAqEtP0Dzd5+iBa1Ki7V1pCxV8ExMdA==", + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.11.tgz", + "integrity": "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-previous": "1.1.2", - "@radix-ui/react-use-size": "1.1.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2605,19 +2783,19 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.14.tgz", - "integrity": "sha512-9bT+FvifX1FK2Mj6UEsTdyu0cN3JaA3KdfhaBao+ONrYFy/pyOy3TU1TNw7iOk1o+0hOEq67RojlUUmoFGwxyA==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.20.tgz", + "integrity": "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2635,15 +2813,15 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.10.tgz", - "integrity": "sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", + "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-slot": "1.3.0" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", @@ -2661,9 +2839,9 @@ } }, "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", - "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2676,9 +2854,9 @@ } }, "node_modules/@radix-ui/react-context": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", - "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2691,16 +2869,16 @@ } }, "node_modules/@radix-ui/react-context-menu": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.3.1.tgz", - "integrity": "sha512-XbrxS68W5dyiE4fAb96yvJwSVU5x66B20A99sD5Mk3xSWK/LqeOnx6TZnim1KieMjXS/CTFq8reOAjWxas2G8Q==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.3.7.tgz", + "integrity": "sha512-CtXP35dxaB5T3zXSd+E3uHe/QpXcpYnZmxp6OaIbfthtfW4wyb77M23BG+bwIJDtsMwEP/YssdsmNyZu7jhWew==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-menu": "2.1.18", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -2718,23 +2896,24 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.17.tgz", - "integrity": "sha512-TDTYmpdq8dI2+Xgvgj9AJ8Ghqq+Eph/TRVEdaFQPDItIY+6QSkU7MJMeevw1568Yw/2Ijz8BTphPSP2XejKphw==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", + "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.10", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-portal": "1.1.12", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -2754,9 +2933,9 @@ } }, "node_modules/@radix-ui/react-direction": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.2.tgz", - "integrity": "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", + "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2769,16 +2948,16 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.13.tgz", - "integrity": "sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-escape-keydown": "1.1.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" }, "peerDependencies": { "@types/react": "*", @@ -2796,18 +2975,18 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.18.tgz", - "integrity": "sha512-PZGV82gFk0WltDRI//SsG28ZIjlo9ANTmoNYg0jLNzXXiDsAy5PkOOYQaVD1pPxY6t7gxffb1QMD6qaUvsBZdw==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz", + "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-menu": "2.1.18", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -2825,9 +3004,9 @@ } }, "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.4.tgz", - "integrity": "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", + "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2840,14 +3019,14 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.10.tgz", - "integrity": "sha512-Fas/lXQqhVvqwAb64s5RFeHiHYElZ6SUQbZaNd6EkfhP/Al7wTIQ9WIR4QVX475tlu5yFCEdDcJH6/UwsZjMWw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-callback-ref": "1.1.2" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2865,17 +3044,17 @@ } }, "node_modules/@radix-ui/react-form": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.10.tgz", - "integrity": "sha512-1NfuvctVtX4sU3Mmq/IdrR8UunxiCMiVg3A5UENKhFzxUBeOyaQQ+lmaQaV7Tc8cqvBKsJL3/KGBsixK0D8WFg==", + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.16.tgz", + "integrity": "sha512-Q4TLEn2A7TAypxwmd6R9EwrlXDvkfYSDMrq9/887AXAGh+G1rH+kYJKSTv+Si9Y0JPKTwKYv6PviAJosysNimA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-label": "2.1.10", - "@radix-ui/react-primitive": "2.1.6" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-label": "2.1.15", + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -2893,20 +3072,20 @@ } }, "node_modules/@radix-ui/react-hover-card": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.17.tgz", - "integrity": "sha512-GjZQIEANVkuuWeztlKz6QEHe31ZX2iDfHzcTMCQVZXC0JyQrgfKWSC+LOOEw6aVV64zyjzobIzSA4AU4eKWrHA==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.23.tgz", + "integrity": "sha512-H8qONfZd3ltrU3+jHCIgITbWo6e1iTKvP9DHdrvYbX48ooRM5FjEDTn16AMwdfuOGkWdZEhpl3PLL/Wk/AnHDQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-popper": "1.3.1", - "@radix-ui/react-portal": "1.1.12", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -2924,12 +3103,12 @@ } }, "node_modules/@radix-ui/react-id": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", - "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2942,12 +3121,12 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.10.tgz", - "integrity": "sha512-ib0zvq2ZsAqKm5tRnqGJn3vOxSgIts5ToxsXT0q1S/GfLD1Zj7UOEnkw8u2w6sRmn47djpQWuSU1DCL1R29/yw==", + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.15.tgz", + "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.6" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -2965,27 +3144,27 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.18.tgz", - "integrity": "sha512-lj8Rxjtn6zJq1oSbE/uDtAwCbB9BnxgHD+8MwJMuTh6u1dPamYhW9iuELr/Z8d0D/UysFblYYHeBPwi7T4k0YQ==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.24.tgz", + "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.10", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.1", - "@radix-ui/react-portal": "1.1.12", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-roving-focus": "1.1.13", - "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -3005,21 +3184,21 @@ } }, "node_modules/@radix-ui/react-menubar": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.18.tgz", - "integrity": "sha512-hX7EGx/oFq6DPY27GQuP/2wP48GHf5LG6r06VgNJlG+znmDS8OfopZcRcGly3L4lsB9FqpmLx6JQSE9P3BUpyw==", + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.24.tgz", + "integrity": "sha512-eeVs0vf7cuqXaM0qLQCPcufImiJNVBXdJDLu7ZGYl2732UH23Qat/foNGrr6vYV3/DdTsBqASoggUFgH14OcZA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-menu": "2.1.18", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-roving-focus": "1.1.13", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -3037,25 +3216,25 @@ } }, "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.16.tgz", - "integrity": "sha512-nJ0SkrSQgudyYhMiYeHA1ayLVuduEJCFLan1RZZN7c9kqzzCFLaU9kuy81uNtqzweM9YaQPgWzxi9MwQ9jZ04g==", + "version": "1.2.22", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.22.tgz", + "integrity": "sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-layout-effect": "1.1.2", - "@radix-ui/react-use-previous": "1.1.2", - "@radix-ui/react-visually-hidden": "1.2.6" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -3073,23 +3252,23 @@ } }, "node_modules/@radix-ui/react-one-time-password-field": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.10.tgz", - "integrity": "sha512-GHkcJ+WVj91At+OvUVTD4R3W0/wxw9t/sG5xFUBYXaCbtWiooZX5Md376QjJqgH4VsVyXrbVNHO2O4NYcmjfVg==", + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.16.tgz", + "integrity": "sha512-Tj9P6ntAJEw52oq/F0AGknXR4XncxEt7XU47O3xJQOiWfLzEy3d9gtgKfvjSzGxzHkfL+VzvxGu2KTFsloJqXw==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-roving-focus": "1.1.13", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-effect-event": "0.0.3", - "@radix-ui/react-use-is-hydrated": "0.1.1", - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3107,19 +3286,19 @@ } }, "node_modules/@radix-ui/react-password-toggle-field": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.5.tgz", - "integrity": "sha512-fVuA82u0b/fClpbEJv8yp1nU9eSvoSEOERsU/hhf3FXGPIvkmE7oEaHEu8poowoXO39/Va7zq2E0TUcYr1dBRg==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.11.tgz", + "integrity": "sha512-4gvFnmDXu3dgj21CqsufzIameRvlRd4SBqaWhcrlrNhRo0Y5i/49AmRJYe1fdAM3G2VNBbmin4b0D6cdQocwgw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-effect-event": "0.0.3", - "@radix-ui/react-use-is-hydrated": "0.1.1" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-is-hydrated": "0.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3137,24 +3316,24 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.17.tgz", - "integrity": "sha512-/YSAOdJ7YJvdn7bn5sdSx2egW+SKY+u7O5RyAVs94Ymrg2fg5QTSFPMRkzvhGyFuE4/qsmPBdrwYoZMZh/4f+g==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.23.tgz", + "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.10", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.1", - "@radix-ui/react-portal": "1.1.12", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-use-controllable-state": "1.2.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -3174,21 +3353,21 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.1.tgz", - "integrity": "sha512-bhnq/0DEPTi2lsOD3J5rTL65qUKHbKbhqHsmN9TMiclSXpipi651ooUKPPp6G5lF/WiHBdn1s0Wuqsn+myVAvw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.2", - "@radix-ui/react-use-rect": "1.1.2", - "@radix-ui/react-use-size": "1.1.2", - "@radix-ui/rect": "1.1.2" + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-rect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", + "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3206,13 +3385,13 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.12.tgz", - "integrity": "sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3230,12 +3409,12 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", - "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3253,12 +3432,12 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.6.tgz", - "integrity": "sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.3.0" + "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", @@ -3276,13 +3455,13 @@ } }, "node_modules/@radix-ui/react-progress": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.10.tgz", - "integrity": "sha512-JYzEg60lk79PwKM27WZyKd7PW8O4OM5jOaFfRPfOyeXmMw7tLJh5kSj+CEjVTehszuwml/AdCzPGMXBTGf4BBw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.16.tgz", + "integrity": "sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-primitive": "2.1.6" + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -3300,21 +3479,20 @@ } }, "node_modules/@radix-ui/react-radio-group": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.1.tgz", - "integrity": "sha512-/SSxZdKEo2Eo29FFRKd06EfFDYp8HryKg0WYg7QLXaydPzl52YfSvCH2a3QDBRdtcuwACroJT8UVjQVgOJ7P9A==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.7.tgz", + "integrity": "sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-roving-focus": "1.1.13", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-previous": "1.1.2", - "@radix-ui/react-use-size": "1.1.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3332,20 +3510,22 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.13.tgz", - "integrity": "sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", + "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3363,20 +3543,20 @@ } }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.12.tgz", - "integrity": "sha512-xuafVzQiTCLsyEjakowTdG3OgTXsmO7IdCiO77otIa+z44xoLNs9Do5eg7POFumIOCjtG6djfm6RKUKpUa/csA==", + "version": "1.2.18", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz", + "integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3394,31 +3574,31 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.1.tgz", - "integrity": "sha512-w6eDvY78LE9ZUiNnXCA1QVK8RYN7k9galFv09kjVydJqBAgHd7Y9A6h0UJ/6DCZNGZMZrB2ohcSW1Bo9d8+wWA==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.7.tgz", + "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.10", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.1", - "@radix-ui/react-portal": "1.1.12", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-layout-effect": "1.1.2", - "@radix-ui/react-use-previous": "1.1.2", - "@radix-ui/react-visually-hidden": "1.2.6", + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -3438,12 +3618,12 @@ } }, "node_modules/@radix-ui/react-separator": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.10.tgz", - "integrity": "sha512-Y6K6jLQCVfCnTL2MEtGxDLffkhNfEfHsEg3Wa8JU+IWdn3EWbLXd3OuOfQRN7p/W/cUce1WyTk3QeuAoDBzN9g==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.15.tgz", + "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.6" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -3461,22 +3641,22 @@ } }, "node_modules/@radix-ui/react-slider": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.1.tgz", - "integrity": "sha512-r91WSpQucNGFKAIxT8FT0H0zyjd5tJlqObLp7LOMV4z49KoDCwjy01w3vDOU4e1wxhF9IgjYco7SB6byOW7Buw==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.7.tgz", + "integrity": "sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.2", - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-layout-effect": "1.1.2", - "@radix-ui/react-use-previous": "1.1.2", - "@radix-ui/react-use-size": "1.1.2" + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3494,12 +3674,12 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.0.tgz", - "integrity": "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.3" + "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", @@ -3512,18 +3692,17 @@ } }, "node_modules/@radix-ui/react-switch": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.1.tgz", - "integrity": "sha512-55bQtCnOB0BohomSHi6qvQXpJEEqUGDm6hRrM0Bph5OXwhSegqkd8IqgBAQkM1IlgUlWZIxpxRcpOEfRIgimyw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.7.tgz", + "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-previous": "1.1.2", - "@radix-ui/react-use-size": "1.1.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3541,19 +3720,19 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.15.tgz", - "integrity": "sha512-kxc9gI6/HfcU4nfMMVS3AmQK414kbU1IE6UCJmMmxjhO3cRPXOyYnmvyKD+ODt7q56nRq9l7Wovi6uaGwKgMlg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz", + "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-roving-focus": "1.1.13", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -3571,23 +3750,23 @@ } }, "node_modules/@radix-ui/react-toast": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.17.tgz", - "integrity": "sha512-uL4kyyWy000pPL43fGGCV5qT6ZchCWEQZOSlkYiPwPt8Hy1iW38RjeptIvz1/SZesrW6Vn58Ct3sV7tfEfiAbw==", + "version": "1.2.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.23.tgz", + "integrity": "sha512-ofhyAsYaocRGOs/n0XWdUOSVzEAG6BfrMVM8z0c0kLEWY38w/0WuMFPTJP/HVaZPYkMvHZoKIIhNcjbTCBILPg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-portal": "1.1.12", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-layout-effect": "1.1.2", - "@radix-ui/react-visually-hidden": "1.2.6" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -3605,14 +3784,14 @@ } }, "node_modules/@radix-ui/react-toggle": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.12.tgz", - "integrity": "sha512-AsAVsYNZIlRBsci7BhE+QyQeKd1h6TffJYt+lF0QQkd5OpQ3klfIByPsCb4G0h/Fq6PJwh1FYNluzBFYzhk4+w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.18.tgz", + "integrity": "sha512-7lonPlKfSacd20GlOBx2ltuVKz9oqWYZz+oMQyOltw6t1y2nyftj2ZmwwUHYn49kqfDWcp8dNZm5NgV+5Z+mug==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -3630,18 +3809,18 @@ } }, "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.13.tgz", - "integrity": "sha512-Xb9PLtlvU66F36LiKba6dFswu6V2mDkgidO4fNSbQHQwmZ9ObxMIO17MN/LJ4aWJecVuSVLAHPZjyeMzJrgeiA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.19.tgz", + "integrity": "sha512-OtnwuSVjd1Ofi+AdnvhsjQdyuhCDwYs1w9RyB5BN/OavXOVQo42SYqQjwUnbPnaiPFBpQ9aX70dWeee+v2oBLA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-roving-focus": "1.1.13", - "@radix-ui/react-toggle": "1.1.12", - "@radix-ui/react-use-controllable-state": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-toggle": "1.1.18", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -3659,18 +3838,18 @@ } }, "node_modules/@radix-ui/react-toolbar": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.13.tgz", - "integrity": "sha512-Za1l4f6fzTkGgz/iynAMN8iaqiKff2wm2/QwiLmHPtDQreWEBrvSimgQFIekxMUdRPhILM7xdIXxuS/o/DGZag==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.19.tgz", + "integrity": "sha512-Ph0IvtYw4VB12ZnZg+YtrGs8yJQsnizwo/zu0R4Y/nWugtJzA7Pg1eWeuDR9+LSqn+xjamss+UOSOJJJ4gx8jw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-roving-focus": "1.1.13", - "@radix-ui/react-separator": "1.1.10", - "@radix-ui/react-toggle-group": "1.1.13" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-separator": "1.1.15", + "@radix-ui/react-toggle-group": "1.1.19" }, "peerDependencies": { "@types/react": "*", @@ -3688,23 +3867,24 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.10.tgz", - "integrity": "sha512-NlNe8D0dWEpVfXFli90IO6X07Josx/b1iu98tDnx9Xv0HT4wLIL+m2VOheMHhK7qbp2HoTBqALEFzGyZs/levw==", + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz", + "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-id": "1.1.2", - "@radix-ui/react-popper": "1.3.1", - "@radix-ui/react-portal": "1.1.12", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-visually-hidden": "1.2.6" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -3722,9 +3902,9 @@ } }, "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz", - "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3737,13 +3917,14 @@ } }, "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", - "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.3", - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3756,12 +3937,12 @@ } }, "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", - "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3774,12 +3955,12 @@ } }, "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.2.tgz", - "integrity": "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.5.tgz", + "integrity": "sha512-ge3ipobwSXTj4JyVtswQ7qZj0ZHdtbGuOno/LrgAAeSxtsJ6Vs4Gz5IkPH2bmqpjcLUFoqGhA/mueuIf63UXlA==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.2" + "@radix-ui/react-use-callback-ref": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3792,9 +3973,9 @@ } }, "node_modules/@radix-ui/react-use-is-hydrated": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.1.tgz", - "integrity": "sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3807,9 +3988,9 @@ } }, "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", - "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3822,9 +4003,9 @@ } }, "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.2.tgz", - "integrity": "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz", + "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -3837,12 +4018,12 @@ } }, "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.2.tgz", - "integrity": "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", + "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", "license": "MIT", "dependencies": { - "@radix-ui/rect": "1.1.2" + "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3855,12 +4036,12 @@ } }, "node_modules/@radix-ui/react-use-size": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.2.tgz", - "integrity": "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", + "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.2" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -3873,12 +4054,12 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.6.tgz", - "integrity": "sha512-jCE0WljWifTI4niIMCll06kGpsJTAPiZVU9H4WR1N6qW7At9ystHbN7dDB+we2xH535roFHj7qKS+RGj0FMDWQ==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz", + "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.6" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -3896,15 +4077,15 @@ } }, "node_modules/@radix-ui/rect": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.2.tgz", - "integrity": "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz", + "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.1.tgz", + "integrity": "sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==", "cpu": [ "arm64" ], @@ -3918,9 +4099,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.1.tgz", + "integrity": "sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==", "cpu": [ "arm64" ], @@ -3934,9 +4115,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.1.tgz", + "integrity": "sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==", "cpu": [ "x64" ], @@ -3950,9 +4131,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.1.tgz", + "integrity": "sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==", "cpu": [ "x64" ], @@ -3966,9 +4147,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.1.tgz", + "integrity": "sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==", "cpu": [ "arm" ], @@ -3982,12 +4163,15 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.1.tgz", + "integrity": "sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3998,12 +4182,15 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.1.tgz", + "integrity": "sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4014,12 +4201,15 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.1.tgz", + "integrity": "sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4030,12 +4220,15 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.1.tgz", + "integrity": "sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4046,12 +4239,15 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.1.tgz", + "integrity": "sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4062,12 +4258,15 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.1.tgz", + "integrity": "sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4078,9 +4277,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.1.tgz", + "integrity": "sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==", "cpu": [ "arm64" ], @@ -4094,48 +4293,76 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.1.tgz", + "integrity": "sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "2.0.0-alpha.3", + "@emnapi/runtime": "2.0.0-alpha.3", + "@napi-rs/wasm-runtime": "^1.2.0" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" } }, "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-alpha.3.tgz", + "integrity": "sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "2.0.1", "tslib": "^2.4.0" } }, "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-alpha.3.tgz", + "integrity": "sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==", "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz", + "integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.1.tgz", + "integrity": "sha512-KjZdi8Q1wh89gsVmghvbrMgWl6ZWmRmHV6wjB7/g4Zf0dyO+hH3neZUtuDNPO00qq5YE5RITVWvrIZKRaAmzGQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.1.tgz", + "integrity": "sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==", "cpu": [ "arm64" ], @@ -4149,9 +4376,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.1.tgz", + "integrity": "sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==", "cpu": [ "x64" ], @@ -4171,9 +4398,9 @@ "license": "MIT" }, "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", "dev": true, "license": "MIT", "dependencies": { @@ -4216,6 +4443,18 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@scure/bip39": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-2.2.0.tgz", @@ -4229,43 +4468,72 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", + "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@simple-libs/child-process-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", - "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", + "integrity": "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==", "dev": true, "license": "MIT", "dependencies": { - "@simple-libs/stream-utils": "^1.2.0" + "@simple-libs/stream-utils": "^2.0.0" }, "engines": { - "node": ">=18" + "node": ">=22" }, "funding": { "url": "https://ko-fi.com/dangreen" } }, "node_modules/@simple-libs/hosted-git-info": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@simple-libs/hosted-git-info/-/hosted-git-info-1.0.2.tgz", - "integrity": "sha512-aAmGQdMH+ZinytKuA2832u0ATeOFNYNk4meBEXtB5xaPotUgggYNhq5tYU/v17wEbmTW5P9iHNqNrFyrhnqBAg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/hosted-git-info/-/hosted-git-info-2.0.0.tgz", + "integrity": "sha512-55XwK/GYgV58PuN8lZFhI1qRxdKoMLJocXl/yM1EPqazFDmM4QWY7q6BxPCPDNKTNunj794DSD4h0H7SrqUdMg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@simple-libs/normalize-package-data": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/normalize-package-data/-/normalize-package-data-1.0.0.tgz", + "integrity": "sha512-/EOmFBekV9tGee8gac/k+5Ox3twkypNqh5TfxA9vTkmcJkjm6f1xqrlstDNOrEhTvnYyVtU6Du2ZhY/IV1+9GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/hosted-git-info": "^2.0.0", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=22" }, "funding": { "url": "https://ko-fi.com/dangreen" } }, "node_modules/@simple-libs/stream-utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", - "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", + "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=22" }, "funding": { "url": "https://ko-fi.com/dangreen" @@ -4275,7 +4543,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@standard-schema/utils": { @@ -4285,9 +4553,9 @@ "license": "MIT" }, "node_modules/@storybook/addon-a11y": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-10.4.6.tgz", - "integrity": "sha512-XCJy+f0DFOiCgUU9knRDlLDxVFI+AAQ3/wE/NF85zB9iDPPS2DwkSN+mas3zDgHt66zhN8Cq3/UiyCDUweV9Zw==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-10.5.7.tgz", + "integrity": "sha512-I30rsNz6aA3xg3811MEry40uJDHP3l5SOkfqtmNkp7y4NdqTDdKhmbhhDAZQX1WWEk6GeMBGr3AJ3TCz7r7JmQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4299,20 +4567,20 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.4.6" + "storybook": "^10.5.7" } }, "node_modules/@storybook/addon-docs": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.4.6.tgz", - "integrity": "sha512-aWAfP5JMiT5a3zBJizwroCRzOCqZwDTJmvsYvwMD3ilIEa/kT1vhf6Xrbk4XIPhDwbh8Hpb/Gfnka1xBYEISWg==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.5.7.tgz", + "integrity": "sha512-KNARJfjICaizinsR3INMEiipZm1ObYo+xw+E26gteu50Bcy2dIZUtk5uHY5XdtardU3AXX6yRXoBZ2HCY3lbHA==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/csf-plugin": "10.4.6", + "@storybook/csf-plugin": "10.5.7", "@storybook/icons": "^2.0.2", - "@storybook/react-dom-shim": "10.4.6", + "@storybook/react-dom-shim": "10.5.7", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" @@ -4323,7 +4591,7 @@ }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.4.6" + "storybook": "^10.5.7" }, "peerDependenciesMeta": { "@types/react": { @@ -4332,9 +4600,9 @@ } }, "node_modules/@storybook/addon-vitest": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@storybook/addon-vitest/-/addon-vitest-10.4.6.tgz", - "integrity": "sha512-VvskHge0GZy86LG6kcY5Ww34z8rDV8JBxqSdUpcJVsWfIvyX6MfAbqI76LlereSyBIJGZJZsqaLwRXsQoVY+0Q==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@storybook/addon-vitest/-/addon-vitest-10.5.7.tgz", + "integrity": "sha512-7NK7Kzazc2vb2h8nGlH15QlUn5J6/LV0xF70VziAK0bxu3r1JupLCoBvckX39vHzFXiAZljXZtt1Wq/OidBxow==", "dev": true, "license": "MIT", "dependencies": { @@ -4349,7 +4617,7 @@ "@vitest/browser": "^3.0.0 || ^4.0.0", "@vitest/browser-playwright": "^4.0.0", "@vitest/runner": "^3.0.0 || ^4.0.0", - "storybook": "^10.4.6", + "storybook": "^10.5.7", "vitest": "^3.0.0 || ^4.0.0" }, "peerDependenciesMeta": { @@ -4368,13 +4636,13 @@ } }, "node_modules/@storybook/builder-vite": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.4.6.tgz", - "integrity": "sha512-BHBtD81HiXUiDQz/CaFynLtWmm7AFUQn8VnXuHipZ8KlnUANopa4yqdVuy/Gwz8ub254uFI5NMZsW/KlgWNgNg==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.5.7.tgz", + "integrity": "sha512-fShF/aQaITqcJuMCLr42BGNUAbhDi4IboqvlbZqXAwgrrTslnZEUnY8GcEcvpZmjl11VwlmazhMJdH50fIgBPg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf-plugin": "10.4.6", + "@storybook/csf-plugin": "10.5.7", "ts-dedent": "^2.0.0" }, "funding": { @@ -4382,14 +4650,14 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^10.4.6", + "storybook": "^10.5.7", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/@storybook/csf-plugin": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.4.6.tgz", - "integrity": "sha512-NILLxDqpA/JR/AazGWpsz+4fadJwRU4uhHephGtYpVOWnQA/DkJfKT6zpcJVq8+QA8A2zKMLX3GVKsXIrxjuDA==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.5.7.tgz", + "integrity": "sha512-IaX8FlM0H36HNFhJ2+4L9bCldqfvHGqcLg841SJNyK/DhfMlM7JsvY/GDH2ZFuWrUf8FSOx96GRRnHq6XfRKag==", "dev": true, "license": "MIT", "dependencies": { @@ -4402,7 +4670,7 @@ "peerDependencies": { "esbuild": "*", "rollup": "*", - "storybook": "^10.4.6", + "storybook": "^10.5.7", "vite": "*", "webpack": "*" }, @@ -4429,25 +4697,24 @@ "license": "MIT" }, "node_modules/@storybook/icons": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-2.0.2.tgz", - "integrity": "sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-2.1.0.tgz", + "integrity": "sha512-Fxh9vYpX9bQqFeHRiY8h2ApeRGDzRSMLwJwNZ/AIRqnyOKHxRKL+yFe+ctEkVJmuptRE9u1Hrn8ZZNHyfDKKNg==", "dev": true, "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/@storybook/react": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.4.6.tgz", - "integrity": "sha512-9Y7YecrVFe1/01KYjfOLxVqTg2Aq+IO6TEv6sC2U0PfD0AWCSCmQ91QqgBpN/XW4aFFWoiZNinyXMUlU8zxy2w==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.5.7.tgz", + "integrity": "sha512-uFvty2MMdFXzW5PcQe1JqDAZkz6cQq7q/9G/cbGVnBEvP6zsOVeL+bmrQ0/WBlFQN0Ko9+ZoCTvaQ9s65zBa5g==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", - "@storybook/react-dom-shim": "10.4.6", + "@storybook/react-dom-shim": "10.5.7", "react-docgen": "^8.0.2", "react-docgen-typescript": "^2.2.2" }, @@ -4460,7 +4727,7 @@ "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.4.6", + "storybook": "^10.5.7", "typescript": ">= 4.9.x" }, "peerDependenciesMeta": { @@ -4476,9 +4743,9 @@ } }, "node_modules/@storybook/react-dom-shim": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.4.6.tgz", - "integrity": "sha512-iGNmKzrq9vgl2PDrYAnZKI+yvac3Ym+lJXXuQaqlFRS23zA5MNm4EBX+rAG7WulqchoK6NaZ0KQOs2mAgEpTMg==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.5.7.tgz", + "integrity": "sha512-lxOkyh+wu/MiBXvYQHjZfD+DRKOa4bHBzbuGuiHXnHXmdOcTRdcrQTsoeN2FPtfugmmOG66cZUEgDwNX+k5eRA==", "dev": true, "license": "MIT", "funding": { @@ -4490,7 +4757,7 @@ "@types/react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.4.6" + "storybook": "^10.5.7" }, "peerDependenciesMeta": { "@types/react": { @@ -4502,19 +4769,19 @@ } }, "node_modules/@storybook/react-vite": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-10.4.6.tgz", - "integrity": "sha512-0arEQtybqGYXHbXpTot+Wv9YtG+V5Vp43QayXavPKQ20M8mpEzhyCPKd0EhqMGSC1Z1UEt0hm365WUBhI9LfKA==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-10.5.7.tgz", + "integrity": "sha512-eEo3eVa2pvqrzQukKxAzx7YvswDAA1s6k/y+tdMxmRvWyHX6QEOsb9Tda6wcVaa7c8BeJM7Ggq+289cRMTH6Iw==", "dev": true, "license": "MIT", "dependencies": { "@joshwooding/vite-plugin-react-docgen-typescript": "^0.7.0", "@rollup/pluginutils": "^5.0.2", - "@storybook/builder-vite": "10.4.6", - "@storybook/react": "10.4.6", + "@storybook/builder-vite": "10.5.7", + "@storybook/react": "10.5.7", "empathic": "^2.0.0", "magic-string": "^0.30.0", - "react-docgen": "^8.0.0", + "react-docgen": "^8.0.2", "resolve": "^1.22.8", "tsconfig-paths": "^4.2.0" }, @@ -4525,52 +4792,58 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^10.4.6", + "storybook": "^10.5.7", + "typescript": ">= 4.9.x", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@tailwindcss/node": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz", - "integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "5.21.6", + "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.3.1" + "tailwindcss": "4.3.3" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz", - "integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.1", - "@tailwindcss/oxide-darwin-arm64": "4.3.1", - "@tailwindcss/oxide-darwin-x64": "4.3.1", - "@tailwindcss/oxide-freebsd-x64": "4.3.1", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.1", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.1", - "@tailwindcss/oxide-linux-x64-musl": "4.3.1", - "@tailwindcss/oxide-wasm32-wasi": "4.3.1", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.1" + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz", - "integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", "cpu": [ "arm64" ], @@ -4584,9 +4857,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz", - "integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", "cpu": [ "arm64" ], @@ -4600,9 +4873,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz", - "integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", "cpu": [ "x64" ], @@ -4616,9 +4889,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz", - "integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", "cpu": [ "x64" ], @@ -4632,9 +4905,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz", - "integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", "cpu": [ "arm" ], @@ -4648,12 +4921,15 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz", - "integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4664,12 +4940,15 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz", - "integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4680,12 +4959,15 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz", - "integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4696,12 +4978,15 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz", - "integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4712,9 +4997,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz", - "integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -4729,9 +5014,9 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.10.0", - "@emnapi/runtime": "^1.10.0", - "@emnapi/wasi-threads": "^1.2.1", + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" @@ -4741,9 +5026,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz", - "integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", "cpu": [ "arm64" ], @@ -4757,9 +5042,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz", - "integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", "cpu": [ "x64" ], @@ -4773,23 +5058,23 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.1.tgz", - "integrity": "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.3.1", - "@tailwindcss/oxide": "4.3.1", - "tailwindcss": "4.3.1" + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "node_modules/@tanstack/eslint-plugin-query": { - "version": "5.101.0", - "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.101.0.tgz", - "integrity": "sha512-wsfg821y4yw21J7nKI2oM5yyGSz3vASXqgWbmWCXZpnyY9ObLrBCcXivwZKj4YHF2fUWiqoOIRX2pbE79cf6gQ==", + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.101.4.tgz", + "integrity": "sha512-jqAZJWXGd5PthJYH9wmC2O5Ry5xAN2/7zxi9qcANQ+Xix8V5JkqNRjZ8Fh+rYzqzVYGiHqbrHekt+uh38OZVpw==", "dev": true, "license": "MIT", "dependencies": { @@ -4826,9 +5111,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.101.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.0.tgz", - "integrity": "sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==", + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", "license": "MIT", "funding": { "type": "github", @@ -4836,12 +5121,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.101.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.0.tgz", - "integrity": "sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==", + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz", + "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.101.0" + "@tanstack/query-core": "5.101.4" }, "funding": { "type": "github", @@ -4890,7 +5175,6 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -4906,9 +5190,9 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", - "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.0.tgz", + "integrity": "sha512-HKAH9C6mBo5yBG6yRO5i43L2iisencAo5z+o5P/saHUoY+miC5ivXRxHBJcFyB5ypPNxHJdK3BoF/3O4DIptMg==", "dev": true, "license": "MIT", "dependencies": { @@ -4920,9 +5204,12 @@ "redent": "^3.0.0" }, "engines": { - "node": ">=14", + "node": ">=22", "npm": ">=6", "yarn": ">=1" + }, + "peerDependencies": { + "@testing-library/dom": ">=10 <11" } }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { @@ -4961,9 +5248,9 @@ } }, "node_modules/@testing-library/user-event": { - "version": "14.6.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", - "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "version": "14.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.3.tgz", + "integrity": "sha512-6dBq67jT8lE+JTE8Exm02Kt6ze43hz1jdiSpSJwtTZiT1xQQ6b7nZYTTQ9njdArdU8XklOwaDp/AbT/eYSKF4g==", "dev": true, "license": "MIT", "engines": { @@ -5016,9 +5303,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "license": "MIT", "optional": true, "dependencies": { @@ -5030,8 +5317,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -5118,29 +5404,22 @@ "license": "MIT" }, "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz", + "integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "24.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", - "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", "devOptional": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~8.3.0" } }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/qrcode": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", @@ -5152,9 +5431,9 @@ } }, "node_modules/@types/react": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", - "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", "devOptional": true, "license": "MIT", "dependencies": { @@ -5162,9 +5441,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", "devOptional": true, "license": "MIT", "peerDependencies": { @@ -5196,17 +5475,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz", - "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz", + "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/type-utils": "8.61.1", - "@typescript-eslint/utils": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/scope-manager": "8.66.0", + "@typescript-eslint/type-utils": "8.66.0", + "@typescript-eslint/utils": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -5219,15 +5498,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.61.1", + "@typescript-eslint/parser": "^8.66.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, "license": "MIT", "engines": { @@ -5235,16 +5514,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", - "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz", + "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/scope-manager": "8.66.0", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0", "debug": "^4.4.3" }, "engines": { @@ -5260,14 +5539,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", - "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz", + "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.61.1", - "@typescript-eslint/types": "^8.61.1", + "@typescript-eslint/tsconfig-utils": "^8.66.0", + "@typescript-eslint/types": "^8.66.0", "debug": "^4.4.3" }, "engines": { @@ -5282,14 +5561,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", - "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.66.0.tgz", + "integrity": "sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1" + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5300,9 +5579,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", - "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz", + "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==", "dev": true, "license": "MIT", "engines": { @@ -5317,15 +5596,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz", - "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz", + "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/utils": "8.61.1", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0", + "@typescript-eslint/utils": "8.66.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -5342,9 +5621,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", - "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.66.0.tgz", + "integrity": "sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==", "dev": true, "license": "MIT", "engines": { @@ -5356,16 +5635,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", - "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz", + "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.61.1", - "@typescript-eslint/tsconfig-utils": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/visitor-keys": "8.61.1", + "@typescript-eslint/project-service": "8.66.0", + "@typescript-eslint/tsconfig-utils": "8.66.0", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/visitor-keys": "8.66.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -5394,26 +5673,26 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -5423,16 +5702,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz", - "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz", + "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.61.1", - "@typescript-eslint/types": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1" + "@typescript-eslint/scope-manager": "8.66.0", + "@typescript-eslint/types": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5447,13 +5726,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", - "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.66.0.tgz", + "integrity": "sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/types": "8.66.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5478,13 +5757,13 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", - "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", + "integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "^1.0.0" + "@rolldown/pluginutils": "^1.0.1" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -5504,15 +5783,15 @@ } }, "node_modules/@vitest/browser": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.9.tgz", - "integrity": "sha512-j1BKtWmPcqpMhmx/L9EPLgAJpCb0zKfwoWLmqBbxaogCXHjOwHFSEoHCBfnGtx93xKQwilZ26m+UOsHqHMkRNg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.10.tgz", + "integrity": "sha512-UDwuWGwXj646CBx/bQHOaJSX7np0I8JL/UKQYa1e4QrVHH8VdWtx8eaOuf8sy0ShwDgR6NjJAsp5eF6vjF6qng==", "dev": true, "license": "MIT", "dependencies": { "@blazediff/core": "1.9.1", - "@vitest/mocker": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/mocker": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pngjs": "^7.0.0", "sirv": "^3.0.2", @@ -5523,18 +5802,18 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.1.9" + "vitest": "4.1.10" } }, "node_modules/@vitest/browser-playwright": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.9.tgz", - "integrity": "sha512-Bq1rOGf9waevzG3EOkO/dene6bvKTUsZMVg8S1i+WH3JcMjuXEjiahP9rAqZRELUqjBySOJsvvSWqK/B3wjKQw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.10.tgz", + "integrity": "sha512-nMoXGEiRpT7m3W7NsbvrM2aKNwiNHZf+zEpUCvMteGjZFvfT96Q9fh7QyB98dvDWXiKvrLxA7bJ1mCOOv+JQPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/browser": "4.1.9", - "@vitest/mocker": "4.1.9", + "@vitest/browser": "4.1.10", + "@vitest/mocker": "4.1.10", "tinyrainbow": "^3.1.0" }, "funding": { @@ -5542,7 +5821,7 @@ }, "peerDependencies": { "playwright": "*", - "vitest": "4.1.9" + "vitest": "4.1.10" }, "peerDependenciesMeta": { "playwright": { @@ -5551,14 +5830,14 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz", - "integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", + "integrity": "sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -5572,8 +5851,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.9", - "vitest": "4.1.9" + "@vitest/browser": "4.1.10", + "vitest": "4.1.10" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -5650,13 +5929,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -5687,9 +5966,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -5700,13 +5979,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -5714,14 +5993,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -5730,9 +6009,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -5740,13 +6019,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -5762,9 +6041,9 @@ "license": "MIT" }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", "bin": { @@ -5784,39 +6063,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", - "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -5848,6 +6094,19 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/argue-cli": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/argue-cli/-/argue-cli-3.1.0.tgz", + "integrity": "sha512-DhBpBfXL4SS2uC0N922MMajKR3CdrTG0u2or1PNYgXMsrSzViJrbtvT0nCLlLGUI0plam/ZZCs7aAauHtW9thw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, "node_modules/aria-hidden": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", @@ -5911,9 +6170,9 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.3.tgz", - "integrity": "sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.4.tgz", + "integrity": "sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==", "dev": true, "license": "MIT", "dependencies": { @@ -5940,9 +6199,9 @@ "license": "MIT" }, "node_modules/axe-core": { - "version": "4.11.4", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.4.tgz", - "integrity": "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz", + "integrity": "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==", "dev": true, "license": "MPL-2.0", "engines": { @@ -5967,9 +6226,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.10.30", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.30.tgz", - "integrity": "sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg==", + "version": "2.10.43", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz", + "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6007,9 +6266,9 @@ } }, "node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -6017,9 +6276,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", "dev": true, "funding": [ { @@ -6037,10 +6296,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -6051,9 +6310,9 @@ } }, "node_modules/builtin-modules": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.2.0.tgz", - "integrity": "sha512-02yxLeyxF4dNl6SlY6/5HfRSrSdZ/sCPoxy2kZNP5dZZX8LSAD9aE2gtJIUgWrsQTiMPl3mxESyrobSwvRGisQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.3.0.tgz", + "integrity": "sha512-hMQUl2bUFG339QygPM97E+mc8OY1IAchORZxm4a/frcYwKzozMzRVDBwHW0NjOqGElLm2O37AVQE8ikxlZHrMQ==", "dev": true, "license": "MIT", "engines": { @@ -6099,9 +6358,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "version": "1.0.30001805", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001805.tgz", + "integrity": "sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==", "dev": true, "funding": [ { @@ -6171,9 +6430,9 @@ } }, "node_modules/chromatic": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-16.10.0.tgz", - "integrity": "sha512-nFsztmnu7rFiGafUJgXvLUNpqmRylz92eNvzBoJNTKKQj4EQUyxznwnfpf1dTs7hXtWD8JwcH92jADydaHA1sw==", + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-18.1.0.tgz", + "integrity": "sha512-I9av8lUc5CQRlYzwKpmOG9cwYvZ4AFmTvtHeNrzOMC1353F9xYw45CHpSNIsNKuOiqqmzci9esXQd5akl0fi6w==", "dev": true, "license": "MIT", "dependencies": { @@ -6184,6 +6443,9 @@ "chromatic": "dist/bin.cjs", "chromatic-cli": "dist/bin.cjs" }, + "engines": { + "node": ">=22.0.0" + }, "peerDependencies": { "@chromatic-com/cypress": "^0.*.* || ^1.0.0", "@chromatic-com/playwright": "^0.*.* || ^1.0.0", @@ -6229,62 +6491,6 @@ "url": "https://polar.sh/cva" } }, - "node_modules/clean-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz", - "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clean-regexp/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", - "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^8.0.0", - "string-width": "^8.2.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -6296,20 +6502,25 @@ } }, "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6319,6 +6530,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6333,6 +6545,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6342,9 +6555,10 @@ } }, "node_modules/cliui/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -6352,7 +6566,10 @@ "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clsx": { @@ -6390,84 +6607,83 @@ "license": "MIT" }, "node_modules/conventional-changelog": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-7.2.0.tgz", - "integrity": "sha512-BEdgG+vPl53EVlTTk9sZ96aagFp0AQ5pw/ggiQMy2SClLbTo1r0l+8dSg79gkLOO5DS1Lswuhp5fWn6RwE+ivg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-8.1.1.tgz", + "integrity": "sha512-my0arqBpzr8mg+7RKDGnw8OiDvBr8/QVJIxazYJlIDeKx/2GCBYxZELaDYpbRhM5gNKfZB8h9SHuetrUFxd5HQ==", "dev": true, "license": "MIT", "dependencies": { - "@conventional-changelog/git-client": "^2.6.0", - "@simple-libs/hosted-git-info": "^1.0.2", - "@types/normalize-package-data": "^2.4.4", - "conventional-changelog-preset-loader": "^5.0.0", - "conventional-changelog-writer": "^8.3.0", - "conventional-commits-parser": "^6.3.0", - "fd-package-json": "^2.0.0", - "meow": "^13.0.0", - "normalize-package-data": "^7.0.0" + "@conventional-changelog/git-client": "^3.1.0", + "@simple-libs/hosted-git-info": "^2.0.0", + "@simple-libs/normalize-package-data": "^1.0.0", + "argue-cli": "^3.1.0", + "conventional-changelog-preset-loader": "^6.0.1", + "conventional-changelog-writer": "^9.2.0", + "conventional-commits-parser": "^7.1.2", + "fd-package-json": "^2.0.0" }, "bin": { "conventional-changelog": "dist/cli/index.js" }, "engines": { - "node": ">=18" + "node": ">=22" } }, "node_modules/conventional-changelog-preset-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-5.0.0.tgz", - "integrity": "sha512-SetDSntXLk8Jh1NOAl1Gu5uLiCNSYenB5tm0YVeZKePRIgDW9lQImromTwLa3c/Gae298tsgOM+/CYT9XAl0NA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-6.0.1.tgz", + "integrity": "sha512-GZ8E3RQzXQb3JFy0pKl8oeSf7ENIhvAMvJr+PlWkavZOesLHHdhkfNJ4SvW35eo1Fu2+EyVxWQ1tVvtzAG2iWg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=22" } }, "node_modules/conventional-changelog-writer": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.4.0.tgz", - "integrity": "sha512-HHBFkk1EECxxmCi4CTu091iuDpQv5/OavuCUAuZmrkWpmYfyD816nom1CvtfXJ/uYfAAjavgHvXHX291tSLK8g==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-9.2.0.tgz", + "integrity": "sha512-eACD5BaAQCYjeI3RExkCZopNaaIuXzCP4kaAb2lEFXcGa/KOuxJfj8v/SnjhvF6377pYn0A2Gji+6GhwUK8rEA==", "dev": true, "license": "MIT", "dependencies": { - "@simple-libs/stream-utils": "^1.2.0", - "conventional-commits-filter": "^5.0.0", - "handlebars": "^4.7.7", - "meow": "^13.0.0", + "@conventional-changelog/template": "^1.2.1", + "@simple-libs/stream-utils": "^2.0.0", + "argue-cli": "^3.1.0", + "conventional-commits-filter": "^6.0.1", "semver": "^7.5.2" }, "bin": { "conventional-changelog-writer": "dist/cli/index.js" }, "engines": { - "node": ">=18" + "node": ">=22" } }, "node_modules/conventional-commits-filter": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", - "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-6.0.1.tgz", + "integrity": "sha512-cs+LadpH7Kpw0M3k8wurk+sOVVDAENA0iK4OBOrkL94j5lEVYRJ4j3zd2bhY9qgzyrPqthdcYT3axzRN7AliMg==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=22" } }, "node_modules/conventional-commits-parser": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz", - "integrity": "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", + "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", "dev": true, "license": "MIT", "dependencies": { - "@simple-libs/stream-utils": "^1.2.0", - "meow": "^13.0.0" + "@simple-libs/stream-utils": "^2.0.0", + "argue-cli": "^3.1.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" }, "engines": { - "node": ">=18" + "node": ">=22" } }, "node_modules/convert-source-map": { @@ -6665,6 +6881,19 @@ "node": ">=6" } }, + "node_modules/detect-indent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -6704,13 +6933,12 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.357", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.357.tgz", - "integrity": "sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g==", + "version": "1.5.389", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz", + "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==", "dev": true, "license": "ISC" }, @@ -6731,9 +6959,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.21.6", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", - "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "version": "5.24.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", + "integrity": "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -6756,19 +6984,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", @@ -6780,9 +6995,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", "dev": true, "license": "MIT" }, @@ -6852,9 +7067,9 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, "license": "MIT", "dependencies": { @@ -6863,8 +7078,8 @@ "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -6977,23 +7192,24 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-10.4.6.tgz", - "integrity": "sha512-CfGSXn6zFspeYTU8R7v797MOmJFj8xc6MWf/oGuRwbKeMoSwnliR+OlXSjMZYM1D6gfmwiuH1VX58LSHdn+ZPg==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-10.5.7.tgz", + "integrity": "sha512-mLpamG1Rsica2jYbUzIZOEuy7Fm1IMtVLMvvxGTpjTVKUMxTXJsANx3MBpH2VSbGQB8Yzlt5399WL/O07K97Ig==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.48.0" + "@typescript-eslint/types": "^8.60.0", + "@typescript-eslint/utils": "^8.60.0" }, "peerDependencies": { "eslint": ">=8", - "storybook": "^10.4.6" + "storybook": "^10.5.7" } }, "node_modules/eslint-plugin-unicorn": { - "version": "64.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-64.0.0.tgz", - "integrity": "sha512-rNZwalHh8i0UfPlhNwg5BTUO1CMdKNmjqe+TgzOTZnpKoi8VBgsW7u9qCHIdpxEzZ1uwrJrPF0uRb7l//K38gA==", + "version": "65.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-65.0.1.tgz", + "integrity": "sha512-daCrQrgxOoOz2uMPWB3Y3vvv/5q+ncwICI8IjoebiwtW87CaY4tAN5EEiRXTYVnf7qi1v1BGBdHOSnZLV0rx6A==", "dev": true, "license": "MIT", "dependencies": { @@ -7001,15 +7217,14 @@ "@eslint-community/eslint-utils": "^4.9.1", "change-case": "^5.4.4", "ci-info": "^4.4.0", - "clean-regexp": "^1.0.0", "core-js-compat": "^3.49.0", + "detect-indent": "^7.0.2", "find-up-simple": "^1.0.1", "globals": "^17.4.0", "indent-string": "^5.0.0", "is-builtin-module": "^5.0.0", "jsesc": "^3.1.0", "pluralize": "^8.0.0", - "regexp-tree": "^0.1.27", "regjsparser": "^0.13.0", "semver": "^7.7.4", "strip-indent": "^4.1.1" @@ -7054,10 +7269,27 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -7078,6 +7310,13 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -7189,17 +7428,10 @@ "node": ">=0.10.0" } }, - "node_modules/eventemitter3": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", - "dev": true, - "license": "MIT" - }, "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -7389,19 +7621,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-nonce": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", @@ -7453,16 +7672,16 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { @@ -7482,9 +7701,9 @@ } }, "node_modules/globals": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", - "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "version": "17.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.9.0.tgz", + "integrity": "sha512-m/MvAW61QVU5VDNF1Vj8axt016h8w7L5TU1e9zlab7XIttAT2YAlCwl75K1fOqvMM9apmD7lbCIRhpfkhmxhCg==", "dev": true, "license": "MIT", "engines": { @@ -7501,37 +7720,15 @@ "license": "ISC" }, "node_modules/graphql": { - "version": "16.14.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.1.tgz", - "integrity": "sha512-cQOsSMS/IrDz82PVyRDvf/Q1F/bRbBVjJlh+xYOkI1qw2bWRvWGiWc+m2O0d6l4Bt1fyY+8kzJ8JFWGJqNeDBg==", + "version": "16.14.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.2.tgz", + "integrity": "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA==", "dev": true, "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, - "node_modules/handlebars": { - "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -7566,13 +7763,6 @@ "set-cookie-parser": "^3.0.1" } }, - "node_modules/headers-polyfill/node_modules/set-cookie-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", - "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", - "dev": true, - "license": "MIT" - }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -7590,26 +7780,6 @@ "hermes-estree": "0.25.1" } }, - "node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", @@ -7631,12 +7801,12 @@ "license": "MIT" }, "node_modules/html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-4.0.1.tgz", + "integrity": "sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==", "license": "MIT", - "dependencies": { - "void-elements": "3.1.0" + "funding": { + "url": "https://locize.com" } }, "node_modules/html5-qrcode": { @@ -7662,9 +7832,9 @@ } }, "node_modules/i18next": { - "version": "26.3.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.1.tgz", - "integrity": "sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==", + "version": "26.3.6", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.6.tgz", + "integrity": "sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==", "funding": [ { "type": "individual", @@ -7682,7 +7852,7 @@ "license": "MIT", "peer": true, "peerDependencies": { - "typescript": "^5 || ^6" + "typescript": "^5 || ^6 || ^7" }, "peerDependenciesMeta": { "typescript": { @@ -7807,22 +7977,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -7955,9 +8109,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -7978,39 +8132,39 @@ } }, "node_modules/jsdom": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", - "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.0.1.tgz", + "integrity": "sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^5.1.11", - "@asamuzakjp/dom-selector": "^7.1.1", + "@asamuzakjp/css-color": "^6.0.5", + "@asamuzakjp/dom-selector": "^8.3.0", "@bramus/specificity": "^2.4.2", - "@csstools/css-syntax-patches-for-csstree": "^1.1.3", - "@exodus/bytes": "^1.15.0", + "@csstools/css-syntax-patches-for-csstree": "^1.1.7", + "@exodus/bytes": "^1.15.1", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.3.5", + "lru-cache": "^11.5.2", "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^6.0.1", - "undici": "^7.25.0", + "tough-cookie": "^6.0.2", + "undici": "^8.9.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.1", + "whatwg-url": "^17.1.0", "xml-name-validator": "^5.0.0" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" }, "peerDependencies": { - "canvas": "^3.0.0" + "canvas": "^3.2.3" }, "peerDependenciesMeta": { "canvas": { @@ -8019,15 +8173,30 @@ } }, "node_modules/jsdom/node_modules/lru-cache": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", - "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.0.tgz", + "integrity": "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.15.1", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^22.14.0 || >=24.0.0" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -8048,13 +8217,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -8075,6 +8237,13 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, "node_modules/jsonfile": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", @@ -8248,6 +8417,9 @@ "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8268,6 +8440,9 @@ "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8288,6 +8463,9 @@ "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8308,6 +8486,9 @@ "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -8362,14 +8543,13 @@ } }, "node_modules/lint-staged": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.7.tgz", - "integrity": "sha512-JrSobt+tW3rH8IOMi8tDZd3foorM5yPEkLD/V2NxobgHrFfHWGee4MOLVuZeScgxftEwbHrPHIFA/ZL+nUJeuA==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.3.0.tgz", + "integrity": "sha512-woZS3vNe3UKqBaLPvbLOtKRY4tLANpWQhom12MGWqC8Mh1lCOO+WgSwmX2amjJAqTY9BkXYW87fCUH5H9Ph6xw==", "dev": true, "license": "MIT", "dependencies": { - "listr2": "^10.2.1", - "picomatch": "^4.0.4", + "picomatch": "^4.0.5", "string-argv": "^0.3.2", "tinyexec": "^1.2.4" }, @@ -8386,23 +8566,6 @@ "yaml": "^2.9.0" } }, - "node_modules/listr2": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.1.tgz", - "integrity": "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^5.2.0", - "eventemitter3": "^5.0.4", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^10.0.0" - }, - "engines": { - "node": ">=22.13.0" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8440,99 +8603,6 @@ "dev": true, "license": "MIT" }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/log-update/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -8551,9 +8621,9 @@ } }, "node_modules/lucide-react": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.20.0.tgz", - "integrity": "sha512-jhXLeC/7m0/tjL1nzMdKk6x256zWA6AtbhTVreHOiKPoeX2d6MK4FbyIQPpVq0E6iPWBisyy1TW+pEge/uMEuQ==", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.30.0.tgz", + "integrity": "sha512-tUIr2jXLbWpCkdtH8XP7P7YppM9ueWgTky99lpWDY6z5REs6B+O6ZQ3U5tHkUUY59ANyOv/PBcs8E4Fe3KO3eA==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -8565,7 +8635,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -8614,32 +8683,6 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -8704,9 +8747,9 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.14.6", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.14.6.tgz", - "integrity": "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.15.0.tgz", + "integrity": "sha512-2wQAmKkQKxRuXvYJxVhPGG0wZNBQyD06oJvxqw90XqLvptdqxdlHrFUfEteKkpaNORX3Xzc+HtEl/q0nfmN2wQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -8749,142 +8792,17 @@ } }, "node_modules/msw-storybook-addon": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/msw-storybook-addon/-/msw-storybook-addon-2.0.7.tgz", - "integrity": "sha512-TGmlxXy2TsaB6QcClVKRxqvay5f93xoLguHOihRFQ+gIEIyiyvcoQjkEeuOe7Y9qvddzGB1LyFomzPo9/EpnuQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/msw-storybook-addon/-/msw-storybook-addon-3.0.0.tgz", + "integrity": "sha512-tyYmYAwSGcfFSHhrE5yzun/OXuVIkBGOt4YTsH5IR94N38F+hzZhvGqe5scu1ENJh/hOApI/8ZRQkWdFNFsKcw==", "dev": true, "license": "MIT", - "dependencies": { - "is-node-process": "^1.0.1" + "bin": { + "msw-storybook-migrate": "build/migrate.mjs" }, "peerDependencies": { - "msw": "^2.0.0" - } - }, - "node_modules/msw/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/msw/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/msw/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/msw/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/msw/node_modules/type-fest": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", - "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "dependencies": { - "tagged-tag": "^1.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/msw/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/msw/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/msw/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/msw/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" + "msw": ">=2", + "storybook": ">=9" } }, "node_modules/mute-stream": { @@ -8898,9 +8816,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "funding": [ { "type": "github", @@ -8922,13 +8840,6 @@ "dev": true, "license": "MIT" }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, "node_modules/next-themes": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", @@ -8940,52 +8851,27 @@ } }, "node_modules/node-releases": { - "version": "2.0.44", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", - "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, - "license": "MIT" - }, - "node_modules/normalize-package-data": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-7.0.1.tgz", - "integrity": "sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^8.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, + "license": "MIT", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=18" } }, "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT" - }, - "node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.20.0" } }, "node_modules/open": { @@ -9071,34 +8957,34 @@ } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.24.2", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.24.2.tgz", + "integrity": "sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.24.2", + "@oxc-resolver/binding-android-arm64": "11.24.2", + "@oxc-resolver/binding-darwin-arm64": "11.24.2", + "@oxc-resolver/binding-darwin-x64": "11.24.2", + "@oxc-resolver/binding-freebsd-x64": "11.24.2", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.24.2", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.24.2", + "@oxc-resolver/binding-linux-arm64-gnu": "11.24.2", + "@oxc-resolver/binding-linux-arm64-musl": "11.24.2", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.24.2", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.24.2", + "@oxc-resolver/binding-linux-riscv64-musl": "11.24.2", + "@oxc-resolver/binding-linux-s390x-gnu": "11.24.2", + "@oxc-resolver/binding-linux-x64-gnu": "11.24.2", + "@oxc-resolver/binding-linux-x64-musl": "11.24.2", + "@oxc-resolver/binding-openharmony-arm64": "11.24.2", + "@oxc-resolver/binding-wasm32-wasi": "11.24.2", + "@oxc-resolver/binding-win32-arm64-msvc": "11.24.2", + "@oxc-resolver/binding-win32-x64-msvc": "11.24.2" } }, "node_modules/p-limit": { @@ -9229,9 +9115,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", - "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -9269,9 +9155,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "engines": { "node": ">=12" @@ -9281,35 +9167,35 @@ } }, "node_modules/playwright": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz", - "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==", + "version": "1.62.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.0.tgz", + "integrity": "sha512-Z14dG305dgaLu6foB1TXQagFiW8JfSUIUaUuPaKQ6NtBPKF1P/qXcqfh6c6K/icPqdy37JmjbiBXf6JNg6Sylw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.61.0" + "playwright-core": "1.62.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=18" + "node": ">=20" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz", - "integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==", + "version": "1.62.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.0.tgz", + "integrity": "sha512-nsNRyq0r2zsG8AcRHWknc9QRA5XCueC7gWMrs+Gx2tlZn9hcl8zudfh00lhJPY1DE7NmZ6bDsT9g2yey8mXljA==", "dev": true, "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/pluralize": { @@ -9333,9 +9219,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "funding": [ { "type": "opencollective", @@ -9352,7 +9238,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -9371,9 +9257,9 @@ } }, "node_modules/prettier": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", - "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -9387,9 +9273,9 @@ } }, "node_modules/prettier-plugin-tailwindcss": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.8.0.tgz", - "integrity": "sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.8.1.tgz", + "integrity": "sha512-iaFMYqDsE4ffdDkn5qup0j5f2aCEBFZrdrZnvu9QKTlWx/iGPeQ4HHu7b7fCPMxeo9nwQBiOAh2nSypdFYWJkw==", "dev": true, "license": "MIT", "engines": { @@ -9471,7 +9357,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -9487,7 +9372,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -9528,6 +9412,78 @@ "node": ">=10.13.0" } }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/qrcode/node_modules/pngjs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", @@ -9537,67 +9493,148 @@ "node": ">=10.13.0" } }, - "node_modules/radix-ui": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.6.0.tgz", - "integrity": "sha512-EUEC70O03EgxWMP5aoqfBZ6iLC5bczFagGy7zhSYRt8o5DP7IWNiP3ywetse3L9b8843ExB0OGWZvgbYVJuNeg==", + "node_modules/qrcode/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.4", - "@radix-ui/react-accessible-icon": "1.1.10", - "@radix-ui/react-accordion": "1.2.14", - "@radix-ui/react-alert-dialog": "1.1.17", - "@radix-ui/react-arrow": "1.1.10", - "@radix-ui/react-aspect-ratio": "1.1.10", - "@radix-ui/react-avatar": "1.2.0", - "@radix-ui/react-checkbox": "1.3.5", - "@radix-ui/react-collapsible": "1.1.14", - "@radix-ui/react-collection": "1.1.10", - "@radix-ui/react-compose-refs": "1.1.3", - "@radix-ui/react-context": "1.1.4", - "@radix-ui/react-context-menu": "2.3.1", - "@radix-ui/react-dialog": "1.1.17", - "@radix-ui/react-direction": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.13", - "@radix-ui/react-dropdown-menu": "2.1.18", - "@radix-ui/react-focus-guards": "1.1.4", - "@radix-ui/react-focus-scope": "1.1.10", - "@radix-ui/react-form": "0.1.10", - "@radix-ui/react-hover-card": "1.1.17", - "@radix-ui/react-label": "2.1.10", - "@radix-ui/react-menu": "2.1.18", - "@radix-ui/react-menubar": "1.1.18", - "@radix-ui/react-navigation-menu": "1.2.16", - "@radix-ui/react-one-time-password-field": "0.1.10", - "@radix-ui/react-password-toggle-field": "0.1.5", - "@radix-ui/react-popover": "1.1.17", - "@radix-ui/react-popper": "1.3.1", - "@radix-ui/react-portal": "1.1.12", - "@radix-ui/react-presence": "1.1.6", - "@radix-ui/react-primitive": "2.1.6", - "@radix-ui/react-progress": "1.1.10", - "@radix-ui/react-radio-group": "1.4.1", - "@radix-ui/react-roving-focus": "1.1.13", - "@radix-ui/react-scroll-area": "1.2.12", - "@radix-ui/react-select": "2.3.1", - "@radix-ui/react-separator": "1.1.10", - "@radix-ui/react-slider": "1.4.1", - "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-switch": "1.3.1", - "@radix-ui/react-tabs": "1.1.15", - "@radix-ui/react-toast": "1.2.17", - "@radix-ui/react-toggle": "1.1.12", - "@radix-ui/react-toggle-group": "1.1.13", - "@radix-ui/react-toolbar": "1.1.13", - "@radix-ui/react-tooltip": "1.2.10", - "@radix-ui/react-use-callback-ref": "1.1.2", - "@radix-ui/react-use-controllable-state": "1.2.3", - "@radix-ui/react-use-effect-event": "0.0.3", - "@radix-ui/react-use-escape-keydown": "1.1.2", - "@radix-ui/react-use-is-hydrated": "0.1.1", - "@radix-ui/react-use-layout-effect": "1.1.2", - "@radix-ui/react-use-size": "1.1.2", - "@radix-ui/react-visually-hidden": "1.2.6" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/radix-ui": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.6.7.tgz", + "integrity": "sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-accessible-icon": "1.1.15", + "@radix-ui/react-accordion": "1.2.20", + "@radix-ui/react-alert-dialog": "1.1.23", + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-aspect-ratio": "1.1.15", + "@radix-ui/react-avatar": "1.2.6", + "@radix-ui/react-checkbox": "1.3.11", + "@radix-ui/react-collapsible": "1.1.20", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-context-menu": "2.3.7", + "@radix-ui/react-dialog": "1.1.23", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-dropdown-menu": "2.1.24", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-form": "0.1.16", + "@radix-ui/react-hover-card": "1.1.23", + "@radix-ui/react-label": "2.1.15", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-menubar": "1.1.24", + "@radix-ui/react-navigation-menu": "1.2.22", + "@radix-ui/react-one-time-password-field": "0.1.16", + "@radix-ui/react-password-toggle-field": "0.1.11", + "@radix-ui/react-popover": "1.1.23", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-progress": "1.1.16", + "@radix-ui/react-radio-group": "1.4.7", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-scroll-area": "1.2.18", + "@radix-ui/react-select": "2.3.7", + "@radix-ui/react-separator": "1.1.15", + "@radix-ui/react-slider": "1.4.7", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-switch": "1.3.7", + "@radix-ui/react-tabs": "1.1.21", + "@radix-ui/react-toast": "1.2.23", + "@radix-ui/react-toggle": "1.1.18", + "@radix-ui/react-toggle-group": "1.1.19", + "@radix-ui/react-toolbar": "1.1.19", + "@radix-ui/react-tooltip": "1.2.16", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-escape-keydown": "1.1.5", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -9615,9 +9652,9 @@ } }, "node_modules/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", - "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9656,21 +9693,21 @@ } }, "node_modules/react-dom": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", - "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.7" + "react": "^19.2.8" } }, "node_modules/react-hook-form": { - "version": "7.79.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.79.0.tgz", - "integrity": "sha512-mhYp/MTmXvzYX6AJcJVko0rktoIhhmRnEouObj4wF5i/tCttgJvnp1+9wRkpITZjDTqpo4IOSJqu0dBlPlV/Lw==", + "version": "7.84.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.84.0.tgz", + "integrity": "sha512-+hWvQP6GLco56mDwrbU4XnHix8t1z90ltZsDIrREl+jnQFQxYLX8oAzqe/Xn8nHpmoXTY5M6oEXrAhbP1qevNQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -9684,19 +9721,19 @@ } }, "node_modules/react-i18next": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.8.tgz", - "integrity": "sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw==", + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.11.tgz", + "integrity": "sha512-cDtkXgxjuFTWUH6V+aQn1Ve5vDiUztCNPWW5GtSHDccsgRXO1nE6QFWCEmc1KAutrb3OUv87wFShJL5RhUwPXg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.29.2", - "html-parse-stringify": "^3.0.1", + "html-parse-stringify": "^4.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 26.2.0", "react": ">= 16.8.0", - "typescript": "^5 || ^6" + "typescript": "^5 || ^6 || ^7" }, "peerDependenciesMeta": { "react-dom": { @@ -9715,8 +9752,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-remove-scroll": { "version": "2.7.2", @@ -9766,9 +9802,9 @@ } }, "node_modules/react-router": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.0.tgz", - "integrity": "sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", + "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -9788,12 +9824,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.0.tgz", - "integrity": "sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz", + "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", "license": "MIT", "dependencies": { - "react-router": "7.18.0" + "react-router": "7.18.2" }, "engines": { "node": ">=20.0.0" @@ -9803,6 +9839,12 @@ "react-dom": ">=18" } }, + "node_modules/react-router/node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/react-style-singleton": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", @@ -9832,9 +9874,9 @@ "license": "MIT" }, "node_modules/recast": { - "version": "0.23.11", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", - "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "version": "0.23.19", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.19.tgz", + "integrity": "sha512-T98lym7kH+pnZmRaD8yDRdaNqyUbwnbEBx0MuchrzMFOEMray4AO3ZJoTUZ5r78Ao78X/OhzW0DL8GB85w/I2w==", "dev": true, "license": "MIT", "dependencies": { @@ -9885,20 +9927,10 @@ "node": ">=8" } }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", - "dev": true, - "license": "MIT", - "bin": { - "regexp-tree": "bin/regexp-tree" - } - }, "node_modules/regjsparser": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", - "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", + "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9971,23 +10003,6 @@ "node": ">=4" } }, - "node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/rettime": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.11.tgz", @@ -9995,20 +10010,13 @@ "dev": true, "license": "MIT" }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.1.tgz", + "integrity": "sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==", "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.142.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -10018,27 +10026,27 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm64": "1.2.1", + "@rolldown/binding-darwin-arm64": "1.2.1", + "@rolldown/binding-darwin-x64": "1.2.1", + "@rolldown/binding-freebsd-x64": "1.2.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.1", + "@rolldown/binding-linux-arm64-gnu": "1.2.1", + "@rolldown/binding-linux-arm64-musl": "1.2.1", + "@rolldown/binding-linux-ppc64-gnu": "1.2.1", + "@rolldown/binding-linux-s390x-gnu": "1.2.1", + "@rolldown/binding-linux-x64-gnu": "1.2.1", + "@rolldown/binding-linux-x64-musl": "1.2.1", + "@rolldown/binding-openharmony-arm64": "1.2.1", + "@rolldown/binding-wasm32-wasi": "1.2.1", + "@rolldown/binding-win32-arm64-msvc": "1.2.1", + "@rolldown/binding-win32-x64-msvc": "1.2.1" } }, "node_modules/rolldown/node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -10077,9 +10085,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -10096,9 +10104,10 @@ "license": "ISC" }, "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz", + "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==", + "dev": true, "license": "MIT" }, "node_modules/sha256-uint8array": { @@ -10165,36 +10174,6 @@ "node": ">=18" } }, - "node_modules/slice-ansi": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", - "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.3", - "is-fullwidth-code-point": "^5.1.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/sonner": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", @@ -10224,42 +10203,6 @@ "node": ">=0.10.0" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", - "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", - "dev": true, - "license": "CC0-1.0" - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -10278,34 +10221,36 @@ } }, "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, "node_modules/storybook": { - "version": "10.4.6", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.4.6.tgz", - "integrity": "sha512-6wkA6LxfDSSilloITsrFOJfsnw0mDUP2h8Ls+lRt8oRsudtz2RWFhLv+Toiwg6NW7hUpdTDc2hzR7DztJid6+A==", + "version": "10.5.7", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.5.7.tgz", + "integrity": "sha512-oiKvWIwIoOhFP1i6dASYyMXwPHKEtVZMshqSB7EvIVYjWRh0l9H7gHEt1z4Gh2rLGFMekWdsm4s94rvwpR7gkg==", "dev": true, "license": "MIT", "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.2", - "@testing-library/jest-dom": "^6.9.1", + "@testing-library/dom": "^10.4.1", + "@testing-library/jest-dom": "6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0 || ^0.28.0", + "jsonc-parser": "^3.3.1", "open": "^10.2.0", "oxc-parser": "^0.127.0", "oxc-resolver": "^11.19.1", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", - "ws": "^8.18.0" + "ws": "^8.21.1" }, "bin": { "storybook": "dist/bin/dispatcher.js" @@ -10317,7 +10262,7 @@ "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "prettier": "^2 || ^3", - "vite-plus": "^0.1.15" + "vite-plus": "^0.1.15 || ^0.2.0" }, "peerDependenciesMeta": { "@types/react": { @@ -10331,6 +10276,26 @@ } } }, + "node_modules/storybook/node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/storybook/node_modules/@vitest/spy": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", @@ -10344,6 +10309,13 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/storybook/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, "node_modules/strict-event-emitter": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", @@ -10361,23 +10333,6 @@ "node": ">=0.6.19" } }, - "node_modules/string-width": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", - "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.5.0", - "strip-ansi": "^7.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-ansi": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", @@ -10500,9 +10455,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz", - "integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", "license": "MIT" }, "node_modules/tapable": { @@ -10585,22 +10540,22 @@ } }, "node_modules/tldts": { - "version": "7.0.30", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", - "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", + "version": "7.4.8", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.8.tgz", + "integrity": "sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.0.30" + "tldts-core": "^7.4.8" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.30", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", - "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", + "version": "7.4.8", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.8.tgz", + "integrity": "sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==", "dev": true, "license": "MIT" }, @@ -10621,9 +10576,9 @@ } }, "node_modules/tough-cookie": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", - "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -10660,9 +10615,9 @@ } }, "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz", + "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==", "dev": true, "license": "MIT", "engines": { @@ -10713,12 +10668,16 @@ } }, "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz", + "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==", + "dev": true, "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, "engines": { - "node": ">=12.20" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10739,16 +10698,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.61.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz", - "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==", + "version": "8.66.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.66.0.tgz", + "integrity": "sha512-QlEbBPz/RuJ1XUHj29nm3t0F/O/cSlEnntozqPOYHnnTGAXFamnMBu5i9Vn6vhUPHGAjR+Vl+5J8vPN/BMUrJw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.61.1", - "@typescript-eslint/parser": "8.61.1", - "@typescript-eslint/typescript-estree": "8.61.1", - "@typescript-eslint/utils": "8.61.1" + "@typescript-eslint/eslint-plugin": "8.66.0", + "@typescript-eslint/parser": "8.66.0", + "@typescript-eslint/typescript-estree": "8.66.0", + "@typescript-eslint/utils": "8.66.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10762,34 +10721,20 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/undici": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", - "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.9.0.tgz", + "integrity": "sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==", "dev": true, "license": "MIT", "engines": { - "node": ">=20.18.1" + "node": ">=22.19.0" } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "devOptional": true, "license": "MIT" }, @@ -10922,27 +10867,16 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", "tinyglobby": "^0.2.17" }, "bin": { @@ -10959,7 +10893,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -11024,20 +10958,281 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -11065,12 +11260,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -11115,16 +11310,16 @@ } }, "node_modules/vitest/node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -11142,15 +11337,6 @@ "node": ">=18" } }, - "node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -11265,48 +11451,10 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-10.0.0.tgz", - "integrity": "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.3", - "string-width": "^8.2.0", - "strip-ansi": "^7.1.2" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", "dev": true, "license": "MIT", "engines": { @@ -11359,10 +11507,14 @@ "license": "MIT" }, "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "license": "ISC" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } }, "node_modules/yallist": { "version": "3.1.1", @@ -11388,105 +11540,49 @@ } }, "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, "license": "MIT", "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/yargs/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -11501,6 +11597,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -11534,11 +11631,23 @@ "type-fest": "^2.19.0" } }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "dev": true, + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 15465d79..5953be25 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "jam", - "version": "2.0.0-beta.0", + "version": "2.0.0-beta.2", "private": true, "description": "Your sats. Your privacy. Your profit.", "repository": "git@github.com:joinmarket-webui/jam.git", "license": "MIT", "type": "module", "engines": { - "node": ">=24.11.1", - "npm": ">=11.6.2" + "node": ">=26.3.0", + "npm": ">=11.16.0" }, "scripts": { "dev": "npm run dev:ng:native", @@ -32,6 +32,7 @@ "test": "NODE_OPTIONS=--no-webstorage vitest run", "test:unit": "NODE_OPTIONS=--no-webstorage vitest run --project unit", "test:storybook": "NODE_OPTIONS=--no-webstorage vitest run --project storybook", + "test:coverage": "npm test -- --coverage.enabled", "check": "npm run format && npm run test && npm run build", "regtest:build": "npm run regtest:clear && npm run regtest:pull && docker compose --env-file docker/regtest/.env.example --file docker/regtest/docker-compose.yml build --pull", "regtest:pull": "docker compose --env-file docker/regtest/.env.example --file docker/regtest/docker-compose.yml pull", @@ -41,14 +42,18 @@ "regtest:down": "docker compose --env-file docker/regtest/.env.example --file docker/regtest/docker-compose.yml down", "regtest:logs:all": "docker compose --env-file docker/regtest/.env.example --file docker/regtest/docker-compose.yml logs --follow", "regtest:logs": "npm run regtest:logs:ng:native:jmwalletd", + "regtest:logs:obwatch": "npm run regtest:logs:ng:obwatch", + "regtest:logs:directory_node": "npm run regtest:logs:ng:directory_node", "regtest:logs:ng:native:jmwalletd": "docker logs -f jm_regtest_joinmarket", "regtest:logs:ng:obwatch": "docker logs -f jm_regtest_joinmarket_ng_orderbook_watcher", - "regtest:logs:obwatch": "npm run regtest:logs:ng:obwatch", - "regtest:logs:ng:directory_node": "docker exec -t jm_regtest_joinmarket_directory_node tail -f /var/log/jam/directory_node_stdout.log -n 200", + "regtest:logs:ng:directory_node": "docker logs -f jm_regtest_joinmarket_ng_directory_node", + "regtest:logs:clientserver:directory_node": "docker exec -t jm_regtest_joinmarket_clientserver_directory_node tail -f /var/log/jam/directory_node_stdout.log -n 200", "regtest:logs:clientserver:native:jmwalletd": "docker exec -t jm_regtest_joinmarket3 tail -f /var/log/jam/jmwalletd_stdout.log", + "regtest:logs:clientserver:native:tor": "docker exec -t jm_regtest_joinmarket3 tail -f /var/log/jam/tor_stdout.log", "regtest:logs:clientserver:native:schedule": "docker exec -t jm_regtest_joinmarket3 tail -f /root/.joinmarket/logs/TUMBLE.schedule", "regtest:init": "./docker/regtest/init-setup.sh", - "regtest:mine": "watch -n 11 ./docker/regtest/mine-block.sh" + "regtest:mine": "watch -n 11 ./docker/regtest/mine-block.sh", + "regtest:listwallets": "docker exec -t jm_regtest_bitcoind bitcoin-cli -datadir=/home/bitcoin/data -regtest -rpcport=43782 listwallets" }, "lint-staged": { "*.{js,jsx,ts,tsx}": [ @@ -66,96 +71,97 @@ ] }, "dependencies": { - "@hookform/resolvers": "5.4.0", - "@joinmarket-webui/joinmarket-api-ts": "0.3.0", - "@noble/hashes": "2.2.0", - "@radix-ui/react-accordion": "1.2.14", - "@radix-ui/react-avatar": "1.2.0", - "@radix-ui/react-checkbox": "1.3.5", - "@radix-ui/react-dialog": "1.1.17", - "@radix-ui/react-dropdown-menu": "2.1.18", - "@radix-ui/react-label": "2.1.10", - "@radix-ui/react-popover": "1.1.17", - "@radix-ui/react-radio-group": "1.4.1", - "@radix-ui/react-select": "2.3.1", - "@radix-ui/react-separator": "1.1.10", - "@radix-ui/react-slot": "1.3.0", - "@radix-ui/react-switch": "1.3.1", - "@radix-ui/react-tabs": "1.1.15", - "@radix-ui/react-tooltip": "1.2.10", + "@hookform/resolvers": "5.7.1", + "@joinmarket-webui/joinmarket-ng-api-ts": "1.0.0", + "@noble/hashes": "2.3.0", + "@radix-ui/react-accordion": "1.2.20", + "@radix-ui/react-avatar": "1.2.6", + "@radix-ui/react-checkbox": "1.3.11", + "@radix-ui/react-dialog": "1.1.23", + "@radix-ui/react-dropdown-menu": "2.1.24", + "@radix-ui/react-label": "2.1.15", + "@radix-ui/react-popover": "1.1.23", + "@radix-ui/react-radio-group": "1.4.7", + "@radix-ui/react-select": "2.3.7", + "@radix-ui/react-separator": "1.1.15", + "@radix-ui/react-slider": "1.4.7", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-switch": "1.3.7", + "@radix-ui/react-tabs": "1.1.21", + "@radix-ui/react-tooltip": "1.2.16", "@scure/bip32": "2.2.0", "@scure/bip39": "2.2.0", - "@tailwindcss/vite": "4.3.1", + "@tailwindcss/vite": "4.3.3", "@tanstack/match-sorter-utils": "8.19.4", - "@tanstack/react-query": "5.101.0", + "@tanstack/react-query": "5.101.4", "@tanstack/react-table": "8.21.3", "bitcoin-address-validation": "3.0.0", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "html5-qrcode": "2.3.8", "i18next-browser-languagedetector": "8.2.1", - "lucide-react": "1.20.0", + "lucide-react": "1.30.0", "next-themes": "0.4.6", "qrcode": "1.5.4", - "radix-ui": "1.6.0", - "react": "19.2.7", - "react-dom": "19.2.7", - "react-hook-form": "7.79.0", - "react-i18next": "17.0.8", - "react-router-dom": "7.18.0", + "radix-ui": "1.6.7", + "react": "19.2.8", + "react-dom": "19.2.8", + "react-hook-form": "7.84.0", + "react-i18next": "17.0.11", + "react-router-dom": "7.18.2", "react-use-websocket": "4.13.0", "sonner": "2.0.7", "tailwind-merge": "3.6.0", - "tailwindcss": "4.3.1", + "tailwindcss": "4.3.3", "tw-animate-css": "1.4.0", "yup": "1.7.1", "zustand": "5.0.14" }, "devDependencies": { - "@chromatic-com/storybook": "5.2.1", - "@eslint/js": "9.39.4", - "@playwright/test": "1.61.0", - "@storybook/addon-a11y": "10.4.6", - "@storybook/addon-docs": "10.4.6", - "@storybook/addon-vitest": "10.4.6", - "@storybook/react-vite": "10.4.6", - "@tanstack/eslint-plugin-query": "5.101.0", - "@testing-library/jest-dom": "6.9.1", + "@chromatic-com/storybook": "5.3.0", + "@eslint/js": "9.39.5", + "@playwright/test": "1.62.0", + "@storybook/addon-a11y": "10.5.7", + "@storybook/addon-docs": "10.5.7", + "@storybook/addon-vitest": "10.5.7", + "@storybook/react-vite": "10.5.7", + "@tanstack/eslint-plugin-query": "5.101.4", + "@testing-library/jest-dom": "7.0.0", "@testing-library/react": "16.3.2", - "@testing-library/user-event": "14.6.1", + "@testing-library/user-event": "14.6.3", "@trivago/prettier-plugin-sort-imports": "6.0.2", - "@types/node": "24.12.4", + "@types/node": "26.1.2", "@types/qrcode": "1.5.6", - "@types/react": "19.2.17", - "@types/react-dom": "19.2.3", - "@vitejs/plugin-react": "6.0.2", - "@vitest/browser": "4.1.9", - "@vitest/browser-playwright": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "conventional-changelog": "7.2.0", - "eslint": "9.39.4", + "@types/react": "19.2.18", + "@types/react-dom": "19.2.4", + "@vitejs/plugin-react": "6.0.5", + "@vitest/browser": "4.1.10", + "@vitest/browser-playwright": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "conventional-changelog": "8.1.1", + "eslint": "9.39.5", "eslint-plugin-compat": "7.0.2", "eslint-plugin-react-hooks": "7.1.1", "eslint-plugin-react-refresh": "0.5.3", - "eslint-plugin-storybook": "10.4.6", - "eslint-plugin-unicorn": "64.0.0", - "globals": "17.6.0", + "eslint-plugin-storybook": "10.5.7", + "eslint-plugin-unicorn": "65.0.1", + "globals": "17.9.0", "husky": "9.1.7", - "jsdom": "29.1.1", - "lint-staged": "17.0.7", - "msw": "2.14.6", - "msw-storybook-addon": "2.0.7", - "prettier": "3.8.4", - "prettier-plugin-tailwindcss": "0.8.0", - "storybook": "10.4.6", + "jsdom": "30.0.1", + "lint-staged": "17.3.0", + "msw": "2.15.0", + "msw-storybook-addon": "3.0.0", + "prettier": "3.9.6", + "prettier-plugin-tailwindcss": "0.8.1", + "storybook": "10.5.7", "typescript": "6.0.3", - "typescript-eslint": "8.61.1", - "vite": "8.0.16", - "vitest": "4.1.9" + "typescript-eslint": "8.66.0", + "vite": "8.2.1", + "vitest": "4.1.10" }, "overrides": { "storybook": "$storybook", - "@joinmarket-webui/joinmarket-api-ts": { + "@joinmarket-webui/joinmarket-ng-api-ts": { "@tanstack/react-query": "$@tanstack/react-query" } }, diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index d20cd4e0..0c970efc 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -7,8 +7,8 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.14.6' -const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' +const PACKAGE_VERSION = '2.15.0' +const INTEGRITY_CHECKSUM = '03cb67ac84128e63d7cd722a6e5b7f1e' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() @@ -98,7 +98,10 @@ addEventListener('fetch', function (event) { // Opening the DevTools triggers the "only-if-cached" request // that cannot be handled by the worker. Bypass such requests. - if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') { + if ( + event.request.cache === 'only-if-cached' && + event.request.mode !== 'same-origin' + ) { return } @@ -121,7 +124,12 @@ addEventListener('fetch', function (event) { async function handleRequest(event, requestId, requestInterceptedAt) { const client = await resolveMainClient(event) const requestCloneForEvents = event.request.clone() - const response = await getResponse(event, client, requestId, requestInterceptedAt) + const response = await getResponse( + event, + client, + requestId, + requestInterceptedAt, + ) // Send back the response clone for the "response:*" life-cycle events. // Ensure MSW is active and ready to handle the message, otherwise @@ -129,8 +137,18 @@ async function handleRequest(event, requestId, requestInterceptedAt) { if (client && activeClientIds.has(client.id)) { const serializedRequest = await serializeRequest(requestCloneForEvents) + // Omit the body of server-sent event stream responses. + // Cloning such responses would prevent client-side stream cancelations + // from reaching the original stream (a teed stream only cancels its + // source once both of its branches cancel) and would buffer the + // entire stream into the unconsumed clone indefinitely. + const isEventStreamResponse = response.headers + .get('content-type') + ?.toLowerCase() + .startsWith('text/event-stream') + // Clone the response so both the client and the library could consume it. - const responseClone = response.clone() + const responseClone = isEventStreamResponse ? null : response.clone() sendToClient( client, @@ -143,15 +161,17 @@ async function handleRequest(event, requestId, requestInterceptedAt) { ...serializedRequest, }, response: { - type: responseClone.type, - status: responseClone.status, - statusText: responseClone.statusText, - headers: Object.fromEntries(responseClone.headers.entries()), - body: responseClone.body, + type: response.type, + status: response.status, + statusText: response.statusText, + headers: Object.fromEntries(response.headers.entries()), + body: responseClone ? responseClone.body : null, }, }, }, - responseClone.body ? [serializedRequest.body, responseClone.body] : [], + responseClone && responseClone.body + ? [serializedRequest.body, responseClone.body] + : [], ) } @@ -216,7 +236,9 @@ async function getResponse(event, client, requestId, requestInterceptedAt) { const acceptHeader = headers.get('accept') if (acceptHeader) { const values = acceptHeader.split(',').map((value) => value.trim()) - const filteredValues = values.filter((value) => value !== 'msw/passthrough') + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) if (filteredValues.length > 0) { headers.set('accept', filteredValues.join(', ')) @@ -287,7 +309,10 @@ function sendToClient(client, message, transferrables = []) { resolve(event.data) } - client.postMessage(message, [channel.port2, ...transferrables.filter(Boolean)]) + client.postMessage(message, [ + channel.port2, + ...transferrables.filter(Boolean), + ]) }) } diff --git a/scripts/changelog.mjs b/scripts/changelog.mjs index c7fa8d14..668038d0 100644 --- a/scripts/changelog.mjs +++ b/scripts/changelog.mjs @@ -58,7 +58,7 @@ const generateChangelog = (newVersion) => { }) changelogStream.on('end', () => { - changelog = changelog.replaceAll(/###\s(\w+)/g, '#### $1').replaceAll('\n\n\n', '\n\n') + changelog = changelog.replaceAll(/###\s(\w+)/g, '#### $1').replaceAll('\n'.repeat(3), '\n\n') const finalChangelog = header + '\n' + (changelog + oldContent).replaceAll(/\n+$/, '\n') fs.writeFileSync(file, finalChangelog, 'utf8') return resolve() diff --git a/src/App.test.tsx b/src/App.test.tsx index b6314dc8..fd8e198c 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -67,6 +67,10 @@ vi.mock('./store/jmSessionStore', () => ({ jmSessionStore: { getState: () => ({ state: holders.jmSession }) }, })) +vi.mock('./store/jmTxStore', () => ({ + jmTxStore: { getState: () => ({ state: {} }) }, +})) + vi.mock('@/store/jamSettingsStore', () => ({ jamSettingsStore: { getState: () => ({ state: { developerMode: holders.developerMode } }) }, useDeveloperMode: () => ({ enabled: holders.developerMode }), @@ -82,11 +86,11 @@ vi.mock('@tanstack/react-query', () => ({ useMutation: () => ({ mutateAsync }), })) -vi.mock('@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query', () => ({ +vi.mock('@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query', () => ({ lockwalletOptions: () => ({ queryKey: ['lock'] }), })) -vi.mock('@joinmarket-webui/joinmarket-api-ts/jm', () => ({ +vi.mock('@joinmarket-webui/joinmarket-ng-api-ts/jm', () => ({ token: vi.fn().mockResolvedValue({ data: { token: 't', refresh_token: 'r' } }), })) diff --git a/src/App.tsx b/src/App.tsx index df450130..d3887a78 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import { lazy, Suspense, useEffect, useMemo, useRef, useState } from 'react' import type { PropsWithChildren } from 'react' -import { lockwalletOptions } from '@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query' -import { token } from '@joinmarket-webui/joinmarket-api-ts/jm' +import { lockwalletOptions } from '@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query' +import { token } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' import { QueryClientProvider, useMutation, useQuery } from '@tanstack/react-query' import type { TFunction } from 'i18next' import { ThemeProvider } from 'next-themes' @@ -45,8 +45,10 @@ import { setIntervalDebounced, walletDisplayName, type WalletFileName } from '@/ import { authStore } from '@/store/authStore' import { jamSettingsStore, useDeveloperMode } from '@/store/jamSettingsStore' import { EarnReportPage } from './components/earn/report/EarnReportPage' +import { RootLayout } from './components/layout/RootLayout' import { LockWalletConfirmDialog } from './components/ui/jam/LockWalletConfirmDialog' import { Spinner } from './components/ui/spinner' +import { TxHistoryPage } from './components/wallet/TxHistoryPage' import { WalletJarsDetailsPage } from './components/wallet/WalletJarsDetailsPage' import { useJamSessionInfoContext } from './context/JamSessionInfoContext' import { JamSessionInfoContextProvider } from './context/JamSessionInfoContextProvider' @@ -54,6 +56,7 @@ import { useJamWalletInfoContext } from './context/JamWalletInfoContext' import { JmWebsocketContextProvider } from './context/JmWebsocketContextProvider' import { getErrorReason } from './lib/errorReason' import { jmSessionStore } from './store/jmSessionStore' +import { jmTxStore, type JmTxInfos } from './store/jmTxStore' import type { Milliseconds } from './types/global' const DevSetupPage = lazy(() => import('@/components/dev/DevSetupPage')) @@ -153,7 +156,15 @@ function App() { const router = createBrowserRouter( createRoutesFromElements( - } errorElement={}> + + + + } + errorElement={} + > : } /> } /> } /> - } /> + } + /> } /> + } /> {isDeveloperMode && isDebugFeatureEnabled('devPage') && ( { const reason = getErrorReason(error, t('global.errors.reason_unknown')) - // TODO: i18n - const errorMessage = t('Error while loading fee values.: {{ reason }}', { reason }) + const errorMessage = t('settings.fees.error_loading_fee_config_failed', { reason }) toast.error(errorMessage) console.error(errorMessage) }) @@ -379,6 +393,7 @@ const RELOAD_WALLET_INFO_DELAY: { AFTER_UTXO_CHANGE: Milliseconds AFTER_BLOCK_HEIGHT_CHANGE: Milliseconds AFTER_TAKER_STOPPED: Milliseconds + AFTER_TX_MESSAGE: Milliseconds } = { // After rescanning, it is necessary to give the JM backend some time to synchronize. // A couple of seconds should be enough, however, this depends on the user hardware @@ -396,6 +411,9 @@ const RELOAD_WALLET_INFO_DELAY: { // Small delay is sufficient after block height change AFTER_TAKER_STOPPED: 210, + + // Small delay is sufficient after an incoming tx message via websocket + AFTER_TX_MESSAGE: 210, } /** @@ -417,6 +435,9 @@ const WalletInfoAutoReload = () => { const previousBlockHeightRef = useRef(currentBlockHeight) const previousTakerRunningRef = useRef(currentTakerRunning) + const jmTxs = useStore(jmTxStore, (state) => state.state) + const previousJmTxsRef = useRef(jmTxs) + const { refetch: refetchWalletBalance, utxosHashHex } = useJamWalletInfoContext() useEffect( @@ -486,6 +507,26 @@ const WalletInfoAutoReload = () => { [refetchWalletBalance, utxosHashHex], ) + useEffect( + function refetchWalletInfoAfterTxMessage() { + const txsChanged = previousJmTxsRef.current !== jmTxs + previousJmTxsRef.current = jmTxs + if (!txsChanged) { + return + } + + const delayBefore = RELOAD_WALLET_INFO_DELAY.AFTER_TX_MESSAGE + console.debug('Trigger refetch looking for funds AFTER_TX_MESSAGE with delay %d...', delayBefore) + + const abortCtrl = new AbortController() + refetchWalletBalance({ delayBefore, signal: abortCtrl.signal }).catch((error: unknown) => { + console.warn('Error while auto-reloading wallet info AFTER_TX_MESSAGE finished', error) + }) + return () => abortCtrl.abort('useEffect(AFTER_TX_MESSAGE) ended') + }, + [refetchWalletBalance, jmTxs], + ) + useEffect( function refetchWalletInfoAfterTakerStopped() { const takerStopped = previousTakerRunningRef.current === true && currentTakerRunning === false diff --git a/src/components/LogsOverlay.tsx b/src/components/LogsOverlay.tsx index 10901ca1..d1a8b7c7 100644 --- a/src/components/LogsOverlay.tsx +++ b/src/components/LogsOverlay.tsx @@ -11,7 +11,7 @@ export function LogsOverlay({ open, onOpenChange }: LogsOverlayProps) { const { t } = useTranslation() return ( onOpenChange(false)}> - + diff --git a/src/components/MainWalletPage.test.tsx b/src/components/MainWalletPage.test.tsx index cfb44ec8..555a5081 100644 --- a/src/components/MainWalletPage.test.tsx +++ b/src/components/MainWalletPage.test.tsx @@ -1,8 +1,8 @@ -import type { ReactNode } from 'react' import { fireEvent, render, screen } from '@testing-library/react' import { describe, expect, it, vi, beforeEach } from 'vitest' import type { Jar } from '@/context/JamWalletInfoContext' import type { WalletFileName } from '@/lib/utils' +import type { JarIndex } from '@/types/global' import MainWalletPage from './MainWalletPage' const navigateMock = vi.fn() @@ -32,23 +32,38 @@ vi.mock('@/context/JamWalletInfoContext', () => ({ useJars: () => ({ jars }), })) -vi.mock('@/lib/utils', () => ({ - cn: (...args: unknown[]) => args.filter(Boolean).join(' '), - shortenStringMiddle: (s: string) => s, - walletDisplayName: (s: string) => s, -})) - -vi.mock('@/components/ui/jam/ClickableJar', () => ({ - ClickableJar: ({ name, onClick }: { name: string; onClick: () => void }) => , +vi.mock('@/store/jamSettingsStore', () => ({ + usePreviewFeatures: () => ({ + 'tx-history': true, + }), })) vi.mock('./wallet/WalletJarsDetailsOverlay', () => ({ - WalletJarsDetailsOverlay: ({ open }: { open: boolean }) => ( -
{open ? 'open' : 'closed'}
+ WalletJarsDetailsOverlay: ({ + open, + onOpenChange, + selectedJarIndex, + }: { + open: boolean + onOpenChange?: (open: boolean) => void + selectedJarIndex?: JarIndex + }) => ( +
+ {open ? 'open' : 'closed'} + {open ? 'open' : 'closed'} + {selectedJarIndex ?? 'undefined'} + +
), })) -vi.mock('./ui/jam/Balance', () => ({ +vi.mock('./wallet/TxHistoryContent', () => ({ + TxHistoryContent: () =>
, +})) + +vi.mock('@/components/ui/jam/Balance', () => ({ Balance: ({ valueString, onClick }: { valueString: string; onClick?: () => void }) => ( {valueString} ), @@ -58,23 +73,6 @@ vi.mock('./ui/spinner', () => ({ Spinner: () =>
, })) -vi.mock('@/components/ui/tooltip', () => ({ - Tooltip: ({ children }: { children?: ReactNode }) =>
{children}
, - TooltipTrigger: ({ children }: { children?: ReactNode }) =>
{children}
, - TooltipContent: ({ children }: { children?: ReactNode }) =>
{children}
, -})) - -vi.mock('@/components/ui/alert', () => ({ - Alert: ({ children }: { children?: ReactNode }) =>
{children}
, - AlertDescription: ({ children }: { children?: ReactNode }) =>
{children}
, -})) - -vi.mock('@/components/ui/button', () => ({ - Button: ({ children, onClick }: { children?: ReactNode; onClick?: () => void }) => ( - - ), -})) - const walletFileName = 'wallet.jmdat' as WalletFileName const makeJar = (jarIndex: number, name: string): Jar => @@ -86,6 +84,7 @@ const makeJar = (jarIndex: number, name: string): Jar => calculatedTotalBalanceInSats: 100, calculatedAvailableBalanceInSats: 100, calculatedFrozenOrLockedBalanceInSats: 0, + calculatedAvailableFrozenBalanceInSats: 0, }, }) as unknown as Jar @@ -105,10 +104,20 @@ describe('MainWalletPage', () => { it('renders balance and jars, and opens the jar overlay on click', () => { render() expect(screen.getByText('5000')).toBeInTheDocument() - expect(screen.getByTestId('overlay')).toHaveTextContent('closed') + expect(screen.getByTestId('WalletJarsDetailsOverlay#open')).toHaveTextContent('closed') + expect(screen.getByTestId('WalletJarsDetailsOverlay#selectedJarIndex')).toHaveTextContent('undefined') - fireEvent.click(screen.getByText('Jar 0')) - expect(screen.getByTestId('overlay')).toHaveTextContent('open') + fireEvent.click(screen.getByText(jars[0].name)) + expect(screen.getByTestId('WalletJarsDetailsOverlay#open')).toHaveTextContent('open') + expect(screen.getByTestId('WalletJarsDetailsOverlay#selectedJarIndex')).toHaveTextContent(String(jars[0].jarIndex)) + + fireEvent.click(screen.getByTestId('WalletJarsDetailsOverlay#onOpenChange')) + expect(screen.getByTestId('WalletJarsDetailsOverlay#open')).toHaveTextContent('closed') + expect(screen.getByTestId('WalletJarsDetailsOverlay#selectedJarIndex')).toHaveTextContent('undefined') + + fireEvent.click(screen.getByTestId('WalletJarsDetailsOverlay#onOpenChange')) + expect(screen.getByTestId('WalletJarsDetailsOverlay#open')).toHaveTextContent('open') + expect(screen.getByTestId('WalletJarsDetailsOverlay#selectedJarIndex')).toHaveTextContent('undefined') }) it('navigates to deposit and withdraw routes', () => { @@ -126,10 +135,14 @@ describe('MainWalletPage', () => { expect(refetch).toHaveBeenCalled() }) - it('shows a jars spinner while fetching and refreshes on click', () => { + it('keeps jars visible and clickable while fetching in the background', () => { walletInfo = { ...walletInfo, isFetching: true } render() - expect(screen.getByTestId('spinner')).toBeInTheDocument() + expect(screen.getByText(jars[0].name)).toBeInTheDocument() + + fireEvent.click(screen.getByText(jars[0].name)) + expect(screen.getByTestId('WalletJarsDetailsOverlay#open')).toHaveTextContent('open') + expect(screen.getByTestId('WalletJarsDetailsOverlay#selectedJarIndex')).toHaveTextContent(String(jars[0].jarIndex)) fireEvent.click(screen.getByText('global.refresh')) expect(refetch).toHaveBeenCalled() }) @@ -139,4 +152,17 @@ describe('MainWalletPage', () => { fireEvent.click(screen.getByText('5000')) expect(toggleDisplayMode).toHaveBeenCalled() }) + + it('expands and collapses transaction history via accordion toggle button', () => { + render() + expect(screen.queryByTestId('TxHistoryContent')).not.toBeInTheDocument() + + const toggleButton = screen.getByTitle('tx_history.expand_history') + fireEvent.click(toggleButton) + expect(screen.getByTestId('TxHistoryContent')).toBeInTheDocument() + + const collapseButton = screen.getByTitle('tx_history.collapse_history') + fireEvent.click(collapseButton) + expect(screen.queryByTestId('TxHistoryContent')).not.toBeInTheDocument() + }) }) diff --git a/src/components/MainWalletPage.tsx b/src/components/MainWalletPage.tsx index 0d4b511e..68b8aa9b 100644 --- a/src/components/MainWalletPage.tsx +++ b/src/components/MainWalletPage.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import { DownloadIcon, InfoIcon, RefreshCwIcon, UploadIcon } from 'lucide-react' +import { ChevronDownIcon, DownloadIcon, InfoIcon, RefreshCwIcon, UploadIcon } from 'lucide-react' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import { Alert, AlertDescription } from '@/components/ui/alert' @@ -14,10 +14,14 @@ import { useWalletBalanceSummary, type Jar as JarObject, } from '@/context/JamWalletInfoContext' +import { getErrorReason } from '@/lib/errorReason' import type { WalletFileName } from '@/lib/utils' import { cn, shortenStringMiddle, walletDisplayName } from '@/lib/utils' +import { usePreviewFeatures } from '@/store/jamSettingsStore' import { Balance } from './ui/jam/Balance' import { Spinner } from './ui/spinner' +import { TxHistoryContent } from './wallet/TxHistoryContent' +import { TxHistoryOverlay } from './wallet/TxHistoryOverlay' import { WalletJarsDetailsOverlay } from './wallet/WalletJarsDetailsOverlay' interface MainWalletPageProps { @@ -27,8 +31,11 @@ interface MainWalletPageProps { export default function MainWalletPage({ walletFileName }: MainWalletPageProps) { const { t } = useTranslation() const navigate = useNavigate() + const previewFeatures = usePreviewFeatures() const [selectedJar, setSelectedJar] = useState() const [isWalletJarsDetailsOpen, setIsWalletJarsDetailsOpen] = useState(false) + const [isTxHistoryOpen, setIsTxHistoryOpen] = useState(false) + const [isHistoryExpanded, setIsHistoryExpanded] = useState(false) const { toggleDisplayMode } = useJamDisplayContext() const { isLoading, isFetching, error, refetch: refetchWalletData } = useJamWalletInfoContext() @@ -54,6 +61,10 @@ export default function MainWalletPage({ walletFileName }: MainWalletPageProps) walletFileName={walletFileName} selectedJarIndex={selectedJar?.jarIndex} /> + {previewFeatures?.['tx-history'] === true ? ( + + ) : null} +

@@ -67,7 +78,7 @@ export default function MainWalletPage({ walletFileName }: MainWalletPageProps) {t('global.loading')}

) : ( -
+
toggleDisplayMode()} valueString={String(walletBalanceSummary.calculatedTotalBalanceInSats)} @@ -76,7 +87,7 @@ export default function MainWalletPage({ walletFileName }: MainWalletPageProps)
)}
-
+
+ + {previewFeatures?.['tx-history'] === true ? ( +
+
+ +
+
+ ) : null}
- {error && ( + {isHistoryExpanded && ( + setIsTxHistoryOpen(true)} + className="w-full max-w-6xl" + /> + )} + + {error ? ( {t('global.errors.error_loading_wallet_failed', { - reason: error.message || t('global.errors.reason_unknown'), + reason: getErrorReason(error, t('global.errors.reason_unknown')), })} - - )} + ) : null}
@@ -115,7 +155,7 @@ export default function MainWalletPage({ walletFileName }: MainWalletPageProps)
- {isFetching ? ( + {isLoading ? (
{t('global.loading')} @@ -146,7 +186,7 @@ export default function MainWalletPage({ walletFileName }: MainWalletPageProps)
- diff --git a/src/components/create/CreateStepConfirm.tsx b/src/components/create/CreateStepConfirm.tsx index fff9b984..729954cf 100644 --- a/src/components/create/CreateStepConfirm.tsx +++ b/src/components/create/CreateStepConfirm.tsx @@ -29,13 +29,7 @@ const createFormSchema = (t: TFunction) => { return yup .object({ revealSensitiveInfo: yup.boolean().required(), - backupConfirmed: yup - .boolean() - .isTrue( - /* TODO: i18n */ t( - 'Please write down your seed phrase and password! Without this information you will not be able to access and recover your wallet!', - ), - ), + backupConfirmed: yup.boolean().isTrue(t('create_wallet.subtitle_wallet_created')), }) .required() } @@ -77,10 +71,10 @@ export const CreateStepConfirm = ({ useEffect(() => { if (backupConfirmed) return - const toastId = toast.info(/* TODO: i18n */ 'Save Your Seed Phrase', { + const toastId = toast.info(t('create_wallet.alert_save_seed_title'), { id: 'save-seed-phrase-reminder', icon: , - description: /* TODO: change i18n key ("alert_description") */ t('create_wallet.subtitle_wallet_created'), + description: t('create_wallet.subtitle_wallet_created'), duration: Number.POSITIVE_INFINITY, }) @@ -118,15 +112,22 @@ export const CreateStepConfirm = ({
- + setValue('revealSensitiveInfo', checked, { shouldValidate: true, shouldTouch: true }) } /> - {t('create_wallet.confirmation_toggle_reveal_info')} + + {t('create_wallet.confirmation_toggle_reveal_info')} + {errors.revealSensitiveInfo?.message && (
{errors.revealSensitiveInfo.message}
@@ -134,15 +135,16 @@ export const CreateStepConfirm = ({
- + setValue('backupConfirmed', checked, { shouldValidate: true })} /> - + {t('create_wallet.confirmation_toggle_info_written_down')} diff --git a/src/components/create/CreateStepVerifyMnemonic.tsx b/src/components/create/CreateStepVerifyMnemonic.tsx index 4612718f..f73a1c74 100644 --- a/src/components/create/CreateStepVerifyMnemonic.tsx +++ b/src/components/create/CreateStepVerifyMnemonic.tsx @@ -87,7 +87,7 @@ export const CreateStepVerifyMnemonic = ({ mnemonicPhrase, onVerified, onBack }:
-
+
{!isCorrect && mnemonicPhrase.map((_, index) => { const word = selectedWords[index] @@ -112,10 +112,13 @@ export const CreateStepVerifyMnemonic = ({ mnemonicPhrase, onVerified, onBack }: return (
{isFilled ? ( - {isHidden ? : word} + {isHidden ? : word} ) : ( ··· )} @@ -135,7 +138,7 @@ export const CreateStepVerifyMnemonic = ({ mnemonicPhrase, onVerified, onBack }: })}
{allSelected && isCorrect && ( -
+
{t('create_wallet.verify_mnemonic.feedback_mnemonic_confirmed')}
@@ -143,7 +146,7 @@ export const CreateStepVerifyMnemonic = ({ mnemonicPhrase, onVerified, onBack }:
{!allSelected && ( -
+
{shuffledWords.map((word, index) => { const isPicked = pickedIndicesSet.has(index) const isWrong = wrongButtonIndex === index @@ -154,7 +157,7 @@ export const CreateStepVerifyMnemonic = ({ mnemonicPhrase, onVerified, onBack }: size="lg" variant={isWrong ? 'destructive' : isPicked ? 'outline' : 'secondary'} disabled={isPicked || wrongButtonIndex !== undefined} - className={cn('font-mono text-sm transition-all', { + className={cn('min-w-0 px-2 font-mono text-sm transition-all', { 'pointer-events-none opacity-25': isPicked, 'animate-shake': isWrong, })} @@ -167,24 +170,21 @@ export const CreateStepVerifyMnemonic = ({ mnemonicPhrase, onVerified, onBack }:
)} -
+
{skipWalletBackupVerification && !isCorrect && (
diff --git a/src/components/create/CreateStepWalletDetails.tsx b/src/components/create/CreateStepWalletDetails.tsx index 2dbdb57a..2c52e5c6 100644 --- a/src/components/create/CreateStepWalletDetails.tsx +++ b/src/components/create/CreateStepWalletDetails.tsx @@ -1,5 +1,6 @@ import type { ComponentProps } from 'react' -import type { SessionResponse } from '@joinmarket-webui/joinmarket-api-ts/jm' +import type { SessionResponse } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' +import { useTranslation } from 'react-i18next' import { Link } from 'react-router-dom' import { Button } from '@/components/ui/button' import { routes } from '@/constants/routes' @@ -12,20 +13,24 @@ type CreateStepWalletDetailsProps = ComponentProps & { } export const CreateStepWalletDetails = ({ sessionInfo, ...createFormProps }: CreateStepWalletDetailsProps) => { + const { t } = useTranslation() + return (
{sessionInfo?.session === true ? ( - + ) : ( )}

- {/* TODO: i18n */} - Already have a wallet?{' '} + {t('create_wallet.text_login_hint')}{' '}

diff --git a/src/components/create/CreateWalletForm.tsx b/src/components/create/CreateWalletForm.tsx index 9e16a7a2..834d735e 100644 --- a/src/components/create/CreateWalletForm.tsx +++ b/src/components/create/CreateWalletForm.tsx @@ -6,7 +6,7 @@ import { useForm, type Mode, type SubmitHandler } from 'react-hook-form' import { useTranslation } from 'react-i18next' import * as yup from 'yup' import { Button } from '@/components/ui/button' -import { Field, FieldLabel } from '@/components/ui/field' +import { Field, FieldError, FieldLabel } from '@/components/ui/field' import { Input } from '@/components/ui/input' import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group' import { Spinner } from '@/components/ui/spinner' @@ -91,89 +91,81 @@ export const CreateWalletForm = ({ return (
void doOnSubmit(event)} className={cn('flex flex-col gap-4', className)} noValidate> -
- - {t('create_wallet.label_wallet_name')} - + {t('create_wallet.label_wallet_name')} + + {errors.walletName?.message && {errors.walletName.message}} + + + + {t('create_wallet.label_password')} + + - - {errors.walletName?.message &&
{errors.walletName.message}
} -
+ + + + + + + + {errors.password?.message && {errors.password.message}} + -
- - {t('create_wallet.label_password')} - - - - - - - - - - - {errors.password?.message &&
{errors.password.message}
} -
- -
- - {t('create_wallet.label_password_confirm')} - - - - - - - - - - - {errors.confirmPassword?.message && ( -
{errors.confirmPassword?.message}
- )} -
+ + {t('create_wallet.label_password_confirm')} + + + + + + + + + + {errors.confirmPassword?.message && {errors.confirmPassword?.message}} + )} - - + + ) } - const isLoading = freezeUtxo.isPending || directSend.isPending - return ( - - {step !== 'select_date' && ( - - )} - - - + : t('global.next') + } + /> ) } return ( - - - - {t('earn.fidelity_bond.create_fidelity_bond.title')} - {t('earn.fidelity_bond.subtitle')} - - - {step !== 'creating' && step !== 'success' && } - - {error && ( - - - {error} - - )} - -
- -
- - {renderFooter()} -
-
+ + + ) } diff --git a/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondDialogSteps.test.tsx b/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondDialogSteps.test.tsx index 0be8d438..fd0afdd3 100644 --- a/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondDialogSteps.test.tsx +++ b/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondDialogSteps.test.tsx @@ -13,20 +13,59 @@ vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => key }), })) -vi.mock('@/components/ui/jam/BitcoinQrCode', () => ({ - BitcoinAddressQrCode: () =>
QR
, -})) - vi.mock('@/lib/utils', () => ({ cn: (...args: unknown[]) => args.filter(Boolean).join(' '), formatSats: (sats: number) => `${sats} sats`, clamp: (val: number, min: number, max: number) => Math.min(Math.max(val, min), max), })) +vi.mock('@/components/ui/jam/Address', () => ({ + Address: ({ value }: { value: string }) => {value}, +})) + +vi.mock('@/components/ui/jam/Balance', () => ({ + Balance: ({ valueString }: { valueString: string }) => {valueString} sats, +})) + vi.mock('@/lib/fidelityBondUtils', () => ({ utxo: { isFidelityBond: vi.fn(), }, + lockdate: { + toDateLabel: () => 'January 1, 2025', + }, +})) + +vi.mock('@/store/jamSettingsStore', () => ({ + useDeveloperMode: () => ({ enabled: false }), +})) + +vi.mock('@/context/JamWalletInfoContext', () => ({ + useJamWalletInfoContext: () => ({ + jars: [ + { jarIndex: 0, name: 'Jar 0', color: '#000', balanceSummary: {}, utxos: [] }, + { jarIndex: 1, name: 'Jar 1', color: '#111', balanceSummary: {}, utxos: [] }, + ], + walletBalanceSummary: { calculatedTotalBalanceInSats: 14000 }, + }), +})) + +vi.mock('@/components/ui/jam/SelectableJar', () => ({ + SelectableJar: ({ + name, + onSelect, + disabled, + isSelected, + }: { + name?: string + onSelect?: () => void + disabled?: boolean + isSelected?: boolean + }) => ( + + ), })) const getBaseWizard = (): Wizard => @@ -34,15 +73,8 @@ const getBaseWizard = (): Wizard => step: 'select_date', setSelectedLockdate: vi.fn(), selectedLockdate: '2025-01', - selectedYear: '2025', - selectedMonth: '01', - minYear: 2024, - minMonth: 1, - yearOptions: [{ value: '2025', label: '2025' }], - monthOptions: [{ value: '01', label: 'Jan' }], - clampLockdate: vi.fn((x: unknown) => x), hasDuplicateLockdate: false, - selectedDateLabel: 'Jan 2025', + existingFbLockdates: [], selectedJarIndex: 0, setSelectedJarIndex: vi.fn(), jarsWithUtxos: [], @@ -51,8 +83,6 @@ const getBaseWizard = (): Wizard => utxoPage: 0, setUtxoPage: vi.fn(), toggleUtxoSelection: vi.fn(), - selectAllUtxos: vi.fn(), - deselectAllUtxos: vi.fn(), totalAmount: 1000, isUsingAllFunds: false, utxosToFreeze: [], @@ -72,7 +102,7 @@ describe('CreateFidelityBondDialogSteps', () => { expect(screen.getByText('earn.fidelity_bond.select_date.description')).toBeInTheDocument() expect(screen.getByText('earn.fidelity_bond.select_date.form_label_month')).toBeInTheDocument() - expect(screen.getByText('Jan 2025')).toBeInTheDocument() + expect(screen.getByText('January 1, 2025')).toBeInTheDocument() }) it('renders select_jar step with jars', () => { @@ -90,8 +120,9 @@ describe('CreateFidelityBondDialogSteps', () => { render() expect(screen.getByText('earn.fidelity_bond.select_jar.title')).toBeInTheDocument() - expect(screen.getByText('Jar 0')).toBeInTheDocument() - expect(screen.getByText('5000 sats')).toBeInTheDocument() + // Jar 0 is eligible, Jar 1 has no eligible utxos and is disabled + expect(screen.getByText('Jar 0')).toBeEnabled() + expect(screen.getByText('Jar 1')).toBeDisabled() }) it('renders select_utxos step', () => { @@ -127,7 +158,7 @@ describe('CreateFidelityBondDialogSteps', () => { expect(screen.getByText('earn.fidelity_bond.review_inputs.label_lock_date')).toBeInTheDocument() expect(screen.getByText('bc1...')).toBeInTheDocument() - expect(screen.getByTestId('qr-code')).toBeInTheDocument() + expect(screen.queryByTestId('qr-code')).not.toBeInTheDocument() }) it('renders creating step', () => { @@ -141,18 +172,18 @@ describe('CreateFidelityBondDialogSteps', () => { it('renders success step', () => { const wizard = getBaseWizard() wizard.step = 'success' - wizard.txResult = { txinfo: { txid: '1234abcd' } } + wizard.txResult = { txinfo: { txid: '1234abcd', hex: '' } } render() expect(screen.getByText('earn.fidelity_bond.create_fidelity_bond.success_text')).toBeInTheDocument() expect(screen.getByText('1234abcd')).toBeInTheDocument() }) - it('warns about a duplicate lock date on the select_date step', () => { + it('passes existing fidelity-bond lock dates to the date selector', () => { const wizard = getBaseWizard() - wizard.hasDuplicateLockdate = true + wizard.existingFbLockdates = ['2025-01'] render() - expect(screen.getByTestId('trans-earn.fidelity_bond.select_date.warning_fb_with_same_expiry')).toBeInTheDocument() + expect(screen.getByText('January 1, 2025')).toBeInTheDocument() }) it('highlights the selected jar and shows the no-jars alert state', () => { @@ -177,7 +208,7 @@ describe('CreateFidelityBondDialogSteps', () => { ] as unknown as Wizard['jarsWithUtxos'] render() expect(screen.getByText('Jar 0')).toBeInTheDocument() - expect(screen.getByText('Jar 1')).toBeInTheDocument() + expect(screen.getByText('Jar 1')).toHaveAttribute('data-selected', 'true') }) it('renders select_utxos with a selected utxo, pagination, and the all-funds warning', () => { diff --git a/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondDialogSteps.tsx b/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondDialogSteps.tsx index 8e3e01d5..fc8b803b 100644 --- a/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondDialogSteps.tsx +++ b/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondDialogSteps.tsx @@ -1,23 +1,7 @@ -import { - AlertTriangleIcon, - CheckCircle2Icon, - CheckIcon, - CopyIcon, - Loader2Icon, - CalendarIcon, - WalletIcon, - CoinsIcon, - LockIcon, -} from 'lucide-react' +import { AlertTriangleIcon, CheckIcon, CalendarIcon, WalletIcon, CoinsIcon, LockIcon } from 'lucide-react' import { Trans } from 'react-i18next' -import { toast } from 'sonner' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' -import { Button } from '@/components/ui/button' -import { buttonVariants } from '@/components/ui/button-variants' import { Card, CardContent } from '@/components/ui/card' -import { BitcoinAddressQrCode } from '@/components/ui/jam/BitcoinQrCode' -import { CopyButton } from '@/components/ui/jam/CopyButton' -import { Label } from '@/components/ui/label' import { Pagination, PaginationContent, @@ -25,11 +9,22 @@ import { PaginationNext, PaginationPrevious, } from '@/components/ui/pagination' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { Spinner } from '@/components/ui/spinner' -import { Switch } from '@/components/ui/switch' import * as fb from '@/lib/fidelityBondUtils' -import { clamp, cn, formatSats } from '@/lib/utils' +import { clamp, cn } from '@/lib/utils' +import { + AddressPreview, + ConfirmationToggle, + CopyableField, + FidelityBondAmount, + InfoCard, + InlineLoading, + JarBadge, + PendingStep, + StepIntro, + SuccessHeading, +} from '../fidelity-bond/FidelityBondDialogParts' +import { FidelityBondJarSelector } from '../fidelity-bond/FidelityBondJarSelector' +import { LockdateSelect } from '../fidelity-bond/LockdateSelect' import type { useCreateFidelityBondWizard } from './useCreateFidelityBondWizard' type Wizard = ReturnType @@ -43,15 +38,7 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial step, setSelectedLockdate, selectedLockdate, - selectedYear, - selectedMonth, - minYear, - minMonth, - yearOptions, - monthOptions, - clampLockdate, - hasDuplicateLockdate, - selectedDateLabel, + existingFbLockdates, selectedJarIndex, setSelectedJarIndex, jarsWithUtxos, @@ -60,8 +47,6 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial utxoPage, setUtxoPage, toggleUtxoSelection, - selectAllUtxos, - deselectAllUtxos, totalAmount, isUsingAllFunds, utxosToFreeze, @@ -74,144 +59,42 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial t, } = wizard + const selectedJar = jarsWithUtxos.find((jar) => jar.jarIndex === selectedJarIndex) + const selectedDateLabel = selectedLockdate ? fb.lockdate.toDateLabel(selectedLockdate) : null + switch (step) { case 'select_date': return (
-
-
- -
-
-

{t('earn.fidelity_bond.select_date.description')}

-

{t('earn.fidelity_bond.select_date.subtitle')}

-
-
+ -
-
- - -
-
- - -
-
- - {selectedLockdate && ( -
-

- {t('earn.fidelity_bond.select_date.label_selected_lock_date')} -

-

{selectedDateLabel}

-
- )} - - {hasDuplicateLockdate && ( - - - - - Warning: A fidelity bond with the same expiry date already exists. - - - - )} +
) case 'select_jar': return (
-
-
- -
-
-

{t('earn.fidelity_bond.select_jar.title')}

-

{t('earn.fidelity_bond.select_jar.description')}

-
-
+ -
- {jarsWithUtxos.map((jar) => { - const isSelected = selectedJarIndex === jar.jarIndex - return ( - setSelectedJarIndex(jar.jarIndex)} - > - -
-
-
-

{jar.name}

-

- {t('earn.fidelity_bond.select_jar.text_available_utxos', { - count: jar.utxos.filter((u) => !u.frozen && !fb.utxo.isFidelityBond(u)).length, - })} -

-
-
-
-

- {formatSats(jar.balanceSummary.calculatedAvailableBalanceInSats)} -

- {isSelected && } -
- - - ) - })} -
+ !jarsWithUtxos.some((eligible) => eligible.jarIndex === jar.jarIndex)} + /> {jarsWithUtxos.length === 0 && ( @@ -224,27 +107,11 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial case 'select_utxos': return (
-
-
- -
-
-

{t('earn.fidelity_bond.select_utxos.title')}

-

- {t('earn.fidelity_bond.select_utxos.description', { jar: selectedJarIndex })} -

-
-
- -
- - -
+
{(() => { @@ -269,7 +136,7 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial
-

- {formatSats(utxo.value)} -

+
@@ -324,9 +192,11 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial {selectedUtxos.length > 0 && (
-
- {t('earn.fidelity_bond.select_utxos.label_total_selected')} - {formatSats(totalAmount)} +
+ + {t('earn.fidelity_bond.select_utxos.label_total_selected')} + +
)} @@ -350,18 +220,14 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial case 'freeze_utxos': return (
-
-
- -
-
-

{t('earn.fidelity_bond.freeze_utxos.title')}

-

{t('earn.fidelity_bond.freeze_utxos.subtitle')}

-
-
+
-
+

{t('earn.fidelity_bond.freeze_utxos.label_selected_utxos', { count: selectedUtxos.length })}

@@ -369,14 +235,14 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial {selectedUtxos.map((utxo) => (
{utxo.utxo.slice(0, 24)}... - {formatSats(utxo.value)} +
))}
{utxosToFreeze.length > 0 && ( -
+

{t('earn.fidelity_bond.freeze_utxos.label_utxos_to_freeze', { count: utxosToFreeze.length })}

@@ -389,7 +255,7 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial {utxosToFreeze.map((utxo) => (
{utxo.utxo.slice(0, 24)}... - {formatSats(utxo.value)} +
))}
@@ -403,57 +269,24 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial return (
-
-
-

- {t('earn.fidelity_bond.review_inputs.label_lock_date')} -

+
+

{selectedDateLabel}

-
-
-

{t('earn.fidelity_bond.review_inputs.label_jar')}

-

- {t('earn.fidelity_bond.review_inputs.label_jar_n', { jar: selectedJarIndex })} -

-
+ + + +
-
-

{t('earn.fidelity_bond.review_inputs.label_amount')}

-

{formatSats(totalAmount)}

-
+ + +
{timelockAddressQuery.isLoading ? ( -
-
- -

{t('earn.fidelity_bond.text_loading')}

-
-
+ ) : ( - address && ( -
-
- -
-
- -
- {address} - } - successText={} - className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'h-10 w-10 shrink-0')} - onSuccess={() => toast.success(t('receive.text_copy_address'))} - onError={() => toast.error(t('receive.error_copy_address_failed'))} - /> -
-
-
- ) + address && )} @@ -467,105 +300,50 @@ export function CreateFidelityBondDialogSteps({ wizard }: CreateFidelityBondDial -
- setConfirmationChecked(checked)} - /> -
- -

- {t('earn.fidelity_bond.create_form.confirmation_toggle_subtitle')} -

-
-
+
) case 'creating': return ( -
-
- -
- -
-
-

{t('earn.fidelity_bond.text_creating')}

-

{t('earn.fidelity_bond.text_creating_subtitle')}

-
+ ) case 'success': return (
-
-
- -
-

{t('earn.fidelity_bond.create_fidelity_bond.success_text')}

-

- {t('earn.fidelity_bond.create_fidelity_bond.text_success_subtitle')} -

-
+
-
-

- {t('earn.fidelity_bond.create_fidelity_bond.label_lock_date')} -

+

{selectedDateLabel}

-
+ {address && ( -
- -
- {address} - } - successText={} - className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'h-10 w-10 shrink-0')} - onSuccess={() => toast.success(t('receive.text_copy_address'))} - onError={() => toast.error(t('receive.error_copy_address_failed'))} - /> -
-
+ )} {txResult?.txinfo?.txid && ( -
- -
- - {txResult.txinfo.txid} - - } - successText={} - className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'h-10 w-10 shrink-0')} - onSuccess={() => - toast.success(t('earn.fidelity_bond.create_fidelity_bond.text_copy_transaction_id')) - } - onError={(error) => { - const reason = - (error instanceof Error ? error.message : undefined) || t('global.errors.reason_unknown') - toast.error(t('global.errors.error_copy_to_clipboard_failed', { reason })) - }} - /> -
-
+ )}
diff --git a/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondFormSchema.ts b/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondFormSchema.ts index b07a30aa..1d4b1e8a 100644 --- a/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondFormSchema.ts +++ b/src/components/earn/CreateFidelityBondDialog/CreateFidelityBondFormSchema.ts @@ -1,6 +1,7 @@ import type { TFunction } from 'i18next' import * as yup from 'yup' import * as fb from '@/lib/fidelityBondUtils' +import { sourceJarField } from '@/lib/formValidation' import type { JarIndex } from '@/types/global' export type SourceValue = { @@ -29,10 +30,8 @@ export const createFidelityBondFormSchema = ( t: TFunction<'translation', undefined>, ): yup.ObjectSchema => { const requiredMessage = t('global.errors.reason_unknown') - const invalidSourceJarFeedbackMessage = t('receive.feedback_invalid_source_jar', { - /* TODO: i18n: provide dedicated fidelity-bond source-jar validation copy */ - defaultValue: 'Please select a source jar.', - }) + /* TODO: i18n: provide dedicated fidelity-bond source-jar validation copy */ + const invalidSourceJarFeedbackMessage = t('receive.feedback_invalid_source_jar') const validLockdates = lockdateOptions.map((option) => option.value) return yup @@ -40,15 +39,7 @@ export const createFidelityBondFormSchema = ( lockdate: yup.string().oneOf(validLockdates, requiredMessage).required(requiredMessage), source: yup .object({ - fromJar: yup - .number() - .integer(invalidSourceJarFeedbackMessage) - .required(invalidSourceJarFeedbackMessage) - .test( - 'valid-source-jar-index-test', - invalidSourceJarFeedbackMessage, - (value) => typeof value === 'number' && jarIndexes.includes(value), - ), + fromJar: sourceJarField(invalidSourceJarFeedbackMessage, (jarIndex) => jarIndexes.includes(jarIndex)), }) .required(), utxoIds: yup.array().of(yup.string().required(requiredMessage)).min(1, requiredMessage).required(requiredMessage), diff --git a/src/components/earn/CreateFidelityBondDialog/StepProgress.tsx b/src/components/earn/CreateFidelityBondDialog/StepProgress.tsx deleted file mode 100644 index 2b628090..00000000 --- a/src/components/earn/CreateFidelityBondDialog/StepProgress.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { cn } from '@/lib/utils' - -type StepProgressProps = { - currentStep: number - totalSteps: number -} - -export function StepProgress({ currentStep, totalSteps }: StepProgressProps) { - return ( -
- {Array.from({ length: totalSteps }, (_, i) => ( -
- ))} -
- ) -} diff --git a/src/components/earn/CreateFidelityBondDialog/types.test.ts b/src/components/earn/CreateFidelityBondDialog/types.test.ts new file mode 100644 index 00000000..21889412 --- /dev/null +++ b/src/components/earn/CreateFidelityBondDialog/types.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest' +import { generateLockdateOptions } from './types' + +describe('generateLockdateOptions', () => { + it.each([false, true])('generates well-formed, ordered YYYY-MM values (developer mode: %s)', (isDeveloperMode) => { + const values = generateLockdateOptions(isDeveloperMode).map((option) => option.value) + + for (const value of values) { + expect(value).toMatch(/^\d{4}-(0[1-9]|1[0-2])$/) + } + // zero-padded YYYY-MM sorts lexicographically === chronologically + expect(values.toSorted()).toEqual(values) + }) + + it('includes the past 12 months in developer mode', () => { + const values = generateLockdateOptions(true).map((option) => option.value) + const now = new Date() + const oneYearAgo = `${now.getUTCFullYear() - 1}-${String(now.getUTCMonth() + 1).padStart(2, '0')}` + + expect(values[0]).toBe(oneYearAgo) + expect(values).toHaveLength(133) // -12 .. +120 months + }) +}) diff --git a/src/components/earn/CreateFidelityBondDialog/types.ts b/src/components/earn/CreateFidelityBondDialog/types.ts index 3fd6332d..916564c7 100644 --- a/src/components/earn/CreateFidelityBondDialog/types.ts +++ b/src/components/earn/CreateFidelityBondDialog/types.ts @@ -24,7 +24,8 @@ export function generateLockdateOptions(isDeveloperMode: boolean): { value: fb.L for (let i = startOffset; i <= 120; i++) { const monthOffset = startMonth + i const year = startYear + Math.floor(monthOffset / 12) - const month = (monthOffset % 12) + 1 + // floored modulo — monthOffset is negative for past months in dev mode + const month = (((monthOffset % 12) + 12) % 12) + 1 const lockdate = `${year}-${month.toString().padStart(2, '0')}` as fb.Lockdate const date = new Date(Date.UTC(year, month - 1, 1)) const label = date.toLocaleDateString(undefined, { year: 'numeric', month: 'long' }) diff --git a/src/components/earn/CreateFidelityBondDialog/useCreateFidelityBondWizard.test.ts b/src/components/earn/CreateFidelityBondDialog/useCreateFidelityBondWizard.test.ts index 6e09b64b..1e7eb60d 100644 --- a/src/components/earn/CreateFidelityBondDialog/useCreateFidelityBondWizard.test.ts +++ b/src/components/earn/CreateFidelityBondDialog/useCreateFidelityBondWizard.test.ts @@ -2,22 +2,31 @@ import { act, renderHook, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { Jar } from '@/context/JamWalletInfoContext' import type { Utxo } from '@/hooks/useQueryUtxos' +import { BALANCE_SUMMARY_EMPTY } from '@/lib/balanceSummary' +import { generateLockdateOptions } from './types' import { useCreateFidelityBondWizard } from './useCreateFidelityBondWizard' -const mocks = vi.hoisted(() => ({ - directSendMutateAsync: vi.fn(), - freezeMutateAsync: vi.fn(), - walletInfoRefetch: vi.fn(), - toastSuccess: vi.fn(), - queryAddress: undefined as string | undefined, - walletInfo: { - jars: [] as Jar[], - fidelityBondSummary: { fbOutputs: [] as Utxo[] }, - refetch: vi.fn(), - }, -})) +const mocks = vi.hoisted(() => { + const addressSummary: Record = { + bcrt1qsource: { status: 'cj-out' }, + } + return { + directSendMutateAsync: vi.fn(), + freezeMutateAsync: vi.fn(), + walletInfoRefetch: vi.fn(), + toastSuccess: vi.fn(), + queryAddress: undefined as string | undefined, + isDeveloperMode: false, + walletInfo: { + jars: [] as Jar[], + fidelityBondSummary: { fbOutputs: [] as Utxo[] }, + addressSummary, + refetch: vi.fn(), + }, + } +}) -vi.mock('@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query', () => ({ +vi.mock('@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query', () => ({ directsendMutation: vi.fn(() => ({ mutationKey: ['directsend'] })), freezeMutation: vi.fn(() => ({ mutationKey: ['freeze'] })), gettimelockaddressOptions: vi.fn(() => ({ queryKey: ['timelock-address'] })), @@ -58,7 +67,7 @@ vi.mock('@/hooks/useApiClient', () => ({ vi.mock('@/store/jamSettingsStore', async (importOriginal) => ({ ...(await importOriginal()), - useDeveloperMode: () => ({ enabled: false }), + useDeveloperMode: () => ({ enabled: mocks.isDeveloperMode }), })) const utxo = (overrides: Partial): Utxo => ({ @@ -82,12 +91,12 @@ const jar = (jarIndex: number, utxos: Utxo[]): Jar => jarIndex, name: `Jar ${jarIndex}`, color: '#808080', - balanceSummary: {}, + balanceSummary: BALANCE_SUMMARY_EMPTY, utxos, }) as unknown as Jar -const lockdateFromWizard = (wizard: ReturnType) => { - const lockdate = wizard.clampLockdate('1900-01') +const minLockdateOption = (isDeveloperMode = false) => { + const lockdate = generateLockdateOptions(isDeveloperMode).at(0)?.value if (!lockdate) throw new Error('Expected a generated lockdate option') return lockdate } @@ -100,7 +109,11 @@ describe('useCreateFidelityBondWizard', () => { mocks.walletInfoRefetch.mockResolvedValue(undefined) mocks.walletInfo.refetch = mocks.walletInfoRefetch mocks.queryAddress = 'bcrt1qtimelockdestination' + mocks.isDeveloperMode = false mocks.walletInfo.fidelityBondSummary = { fbOutputs: [] } + mocks.walletInfo.addressSummary = { + bcrt1qsource: { status: 'cj-out' }, + } mocks.walletInfo.jars = [ jar(0, [ utxo({ utxo: 'small:0', value: 10_000 }), @@ -117,26 +130,26 @@ describe('useCreateFidelityBondWizard', () => { ] }) - it('derives selectable utxos and selection totals from wallet state', async () => { + it('derives selectable utxos and keeps selection to one utxo', async () => { const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) act(() => result.current.setSelectedJarIndex(0)) expect(result.current.availableUtxos.map((entry) => entry.utxo)).toEqual(['large:0', 'small:0']) - act(() => result.current.selectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) - expect(result.current.selectedUtxos.map((entry) => entry.utxo)).toEqual(['large:0', 'small:0']) - expect(result.current.totalAmount).toBe(60_000) - expect(result.current.utxosToFreeze.map((entry) => entry.utxo)).toEqual([]) + expect(result.current.selectedUtxos.map((entry) => entry.utxo)).toEqual(['large:0']) + expect(result.current.totalAmount).toBe(50_000) + expect(result.current.utxosToFreeze.map((entry) => entry.utxo)).toEqual(['small:0']) expect(result.current.isUsingAllFunds).toBe(false) - act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[1])) expect(result.current.selectedUtxos.map((entry) => entry.utxo)).toEqual(['small:0']) expect(result.current.utxosToFreeze.map((entry) => entry.utxo)).toEqual(['large:0']) - act(() => result.current.deselectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[1])) await waitFor(() => expect(result.current.selectedUtxos).toHaveLength(0)) }) @@ -144,7 +157,7 @@ describe('useCreateFidelityBondWizard', () => { it('skips jar selection when only one jar has eligible utxos', async () => { mocks.walletInfo.jars = [jar(0, [utxo({ utxo: 'only:0', value: 25_000 })])] const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) - const lockdate = lockdateFromWizard(result.current) + const lockdate = minLockdateOption() act(() => result.current.setSelectedLockdate(lockdate)) await act(async () => result.current.handleNext()) @@ -152,7 +165,7 @@ describe('useCreateFidelityBondWizard', () => { expect(result.current.step).toBe('select_utxos') expect(result.current.selectedJarIndex).toBe(0) - act(() => result.current.selectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) await act(async () => result.current.handleNext()) expect(result.current.step).toBe('review') @@ -176,7 +189,7 @@ describe('useCreateFidelityBondWizard', () => { it('freezes unselected utxos before review and supports back navigation', async () => { const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) - const lockdate = lockdateFromWizard(result.current) + const lockdate = minLockdateOption() act(() => result.current.setSelectedLockdate(lockdate)) await act(async () => result.current.handleNext()) @@ -211,7 +224,11 @@ describe('useCreateFidelityBondWizard', () => { act(() => { result.current.setStep('success') result.current.setSelectedJarIndex(0) - result.current.selectAllUtxos() + }) + act(() => { + result.current.toggleUtxoSelection(result.current.availableUtxos[0]) + }) + act(() => { result.current.handleOpenChange(false) }) @@ -235,40 +252,8 @@ describe('useCreateFidelityBondWizard', () => { expect(result.current.step).toBe('review') }) - it('exposes lockdate boundaries and clamps out-of-range values', () => { - const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) - - const { minYear, minMonth, yearOptions, monthOptions } = result.current - expect(minYear).toBeGreaterThan(0) - expect(minMonth).toBeGreaterThanOrEqual(1) - expect(yearOptions.length).toBeGreaterThan(0) - expect(monthOptions.length).toBe(12) - - const min = result.current.clampLockdate('1900-01') - const max = result.current.clampLockdate('9999-12') - expect(min).not.toBe('') - expect(max).not.toBe('') - expect(min < max).toBe(true) - - // empty input clamps to min - expect(result.current.clampLockdate('')).toBe(min) - // a value already inside the range is returned untouched - expect(result.current.clampLockdate(min)).toBe(min) - - // derived year/month default to empty before a selection - expect(result.current.selectedYear).toBe('') - expect(result.current.selectedMonth).toBe('') - - act(() => result.current.setSelectedLockdate(min)) - - expect(result.current.selectedYear).toBe(min.slice(0, 4)) - expect(result.current.selectedMonth).toBe(min.slice(5, 7)) - expect(result.current.selectedDateLabel).not.toBeNull() - }) - it('flags duplicate lockdates against existing fidelity bonds', () => { - const { result: probe } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) - const lockdate = lockdateFromWizard(probe.current) + const lockdate = minLockdateOption() const timestampSeconds = Math.floor(new Date(`${lockdate}-01T00:00:00Z`).getTime() / 1000) mocks.walletInfo.fidelityBondSummary = { @@ -287,6 +272,31 @@ describe('useCreateFidelityBondWizard', () => { expect(result.current.hasDuplicateLockdate).toBeFalsy() act(() => result.current.setSelectedLockdate(lockdate)) expect(result.current.hasDuplicateLockdate).toBe(true) + expect(result.current.canProceed()).toBe(false) + }) + + it('blocks a lock date already used by an expired fidelity bond', () => { + mocks.isDeveloperMode = true + const expiredLockdate = minLockdateOption(true) + const timestampSeconds = Math.floor(new Date(`${expiredLockdate}-01T00:00:00Z`).getTime() / 1000) + + mocks.walletInfo.fidelityBondSummary = { + fbOutputs: [ + utxo({ + utxo: 'expired-bond:0', + value: 100_000, + locktime: `${expiredLockdate}-01 00:00:00`, + path: `m/84'/1'/0'/0/0:${timestampSeconds}`, + }), + ], + } + + const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) + + expect(result.current.existingFbLockdates).toContain(expiredLockdate) + act(() => result.current.setSelectedLockdate(expiredLockdate)) + expect(result.current.hasDuplicateLockdate).toBe(true) + expect(result.current.canProceed()).toBe(false) }) it('ignores existing fidelity bonds whose locktime cannot be derived', () => { @@ -303,7 +313,7 @@ describe('useCreateFidelityBondWizard', () => { } const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) - const lockdate = lockdateFromWizard(result.current) + const lockdate = minLockdateOption() act(() => result.current.setSelectedLockdate(lockdate)) expect(result.current.hasDuplicateLockdate).toBeFalsy() @@ -313,8 +323,8 @@ describe('useCreateFidelityBondWizard', () => { const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) act(() => result.current.setSelectedJarIndex(0)) - act(() => result.current.selectAllUtxos()) - expect(result.current.selectedUtxos.map((entry) => entry.utxo)).toEqual(['large:0', 'small:0']) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) + expect(result.current.selectedUtxos.map((entry) => entry.utxo)).toEqual(['large:0']) // switch to a jar that does not contain the previously selected ids act(() => result.current.setSelectedJarIndex(1)) @@ -322,9 +332,26 @@ describe('useCreateFidelityBondWizard', () => { await waitFor(() => expect(result.current.selectedUtxos).toEqual([])) }) + it('only exposes unfrozen cj-out utxos for fidelity-bond creation', () => { + mocks.walletInfo.addressSummary = { + bcrt1qcj: { status: 'cj-out' }, + bcrt1qdeposit: { status: 'deposit' }, + } + mocks.walletInfo.jars = [ + jar(0, [utxo({ utxo: 'cj:0', address: 'bcrt1qcj' }), utxo({ utxo: 'deposit:0', address: 'bcrt1qdeposit' })]), + jar(1, [utxo({ utxo: 'deposit-only:0', address: 'bcrt1qdeposit', mixdepth: 1 })]), + ] + + const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) + + expect(result.current.jarsWithUtxos.map((entry) => entry.jarIndex)).toEqual([0]) + act(() => result.current.setSelectedJarIndex(0)) + expect(result.current.availableUtxos.map((entry) => entry.utxo)).toEqual(['cj:0']) + }) + it('reflects canProceed gating for each step', () => { const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) - const lockdate = lockdateFromWizard(result.current) + const lockdate = minLockdateOption() // select_date expect(result.current.canProceed()).toBe(false) @@ -340,7 +367,7 @@ describe('useCreateFidelityBondWizard', () => { // select_utxos act(() => result.current.setStep('select_utxos')) expect(result.current.canProceed()).toBe(false) - act(() => result.current.selectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) expect(result.current.canProceed()).toBe(true) // freeze_utxos always proceeds @@ -419,19 +446,16 @@ describe('useCreateFidelityBondWizard', () => { expect(result.current.step).toBe('select_utxos') }) - it('skips freezing and moves straight to review when all utxos are selected', async () => { + it('skips freezing and moves straight to review when the selected utxo is the only one in the jar', async () => { + mocks.walletInfo.jars = [jar(0, [utxo({ utxo: 'only:0', value: 25_000 })])] const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) - const lockdate = lockdateFromWizard(result.current) + const lockdate = minLockdateOption() act(() => result.current.setSelectedLockdate(lockdate)) await act(async () => result.current.handleNext()) - expect(result.current.step).toBe('select_jar') - - act(() => result.current.setSelectedJarIndex(0)) - await act(async () => result.current.handleNext()) expect(result.current.step).toBe('select_utxos') - act(() => result.current.selectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) await act(async () => result.current.handleNext()) expect(result.current.step).toBe('review') @@ -443,7 +467,7 @@ describe('useCreateFidelityBondWizard', () => { const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) act(() => result.current.setSelectedJarIndex(0)) - act(() => result.current.selectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) act(() => result.current.setStep('review')) // confirmation not accepted -> trigger() fails the whole form await act(async () => result.current.handleNext()) @@ -467,7 +491,7 @@ describe('useCreateFidelityBondWizard', () => { const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) act(() => result.current.setSelectedJarIndex(0)) - act(() => result.current.selectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) act(() => result.current.setStep('select_utxos')) act(() => result.current.handleBack()) @@ -481,7 +505,7 @@ describe('useCreateFidelityBondWizard', () => { const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) act(() => result.current.setSelectedJarIndex(0)) - act(() => result.current.selectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) act(() => result.current.setStep('select_utxos')) act(() => result.current.handleBack()) @@ -500,10 +524,11 @@ describe('useCreateFidelityBondWizard', () => { }) it('returns from review to select_utxos when nothing was frozen', () => { + mocks.walletInfo.jars = [jar(0, [utxo({ utxo: 'only:0', value: 25_000 })])] const { result } = renderHook(() => useCreateFidelityBondWizard(true, vi.fn(), 'wallet.jmdat')) act(() => result.current.setSelectedJarIndex(0)) - act(() => result.current.selectAllUtxos()) + act(() => result.current.toggleUtxoSelection(result.current.availableUtxos[0])) act(() => result.current.setStep('review')) expect(result.current.utxosToFreeze).toEqual([]) act(() => result.current.handleBack()) diff --git a/src/components/earn/CreateFidelityBondDialog/useCreateFidelityBondWizard.ts b/src/components/earn/CreateFidelityBondDialog/useCreateFidelityBondWizard.ts index 81425e1f..b19b0f49 100644 --- a/src/components/earn/CreateFidelityBondDialog/useCreateFidelityBondWizard.ts +++ b/src/components/earn/CreateFidelityBondDialog/useCreateFidelityBondWizard.ts @@ -1,30 +1,30 @@ import { useState, useMemo } from 'react' import { yupResolver } from '@hookform/resolvers/yup' -import { - directsendMutation, - freezeMutation, - gettimelockaddressOptions, -} from '@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query' -import type { DirectSendResponse, ErrorMessage } from '@joinmarket-webui/joinmarket-api-ts/jm' -import { useMutation, useQuery } from '@tanstack/react-query' +import { gettimelockaddressOptions } from '@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query' +import type { DirectSendResponse } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' +import { useQuery } from '@tanstack/react-query' import { useForm, useWatch } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' +import { + JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS, + JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS_DELAY, +} from '@/constants/jam' import { useJamWalletInfoContext } from '@/context/JamWalletInfoContext' import { useApiClient } from '@/hooks/useApiClient' import type { Utxo } from '@/hooks/useQueryUtxos' -import { getErrorReason } from '@/lib/errorReason' import * as fb from '@/lib/fidelityBondUtils' -import type { WalletFileName } from '@/lib/utils' +import { delayedPromise, type WalletFileName } from '@/lib/utils' import { useDeveloperMode } from '@/store/jamSettingsStore' import type { JarIndex } from '@/types/global' +import { useFidelityBondMutations } from '../fidelity-bond/useFidelityBondMutations' import { CREATE_FIDELITY_BOND_FORM_DEFAULT_VALUES, createFidelityBondFormSchema, type CreateFidelityBondFormValues, } from './CreateFidelityBondFormSchema' import type { Step } from './types' -import { generateLockdateOptions, getYearOptions, getMonthOptions } from './types' +import { generateLockdateOptions } from './types' export function useCreateFidelityBondWizard( open: boolean, @@ -40,13 +40,29 @@ export function useCreateFidelityBondWizard( const [frozenUtxos, setFrozenUtxos] = useState([]) const [utxoPage, setUtxoPage] = useState(0) const [txResult, setTxResult] = useState() - const [error, setError] = useState() + + const { freezeUtxo, unfreezeUtxo, directSend, error, setError } = useFidelityBondMutations({ + unfreezeErrorKey: 'earn.fidelity_bond.error_unfreezing_utxos', + sendErrorKey: 'earn.fidelity_bond.error_creating_fidelity_bond', + }) const lockdateOptions = useMemo(() => generateLockdateOptions(isDeveloperMode), [isDeveloperMode]) + const existingFbLockdates = useMemo(() => { + return walletInfo.fidelityBondSummary.fbOutputs + .map((fbUtxo) => { + const locktime = fb.utxo.getLocktime(fbUtxo) + return locktime ? fb.lockdate.fromTimestamp(locktime) : null + }) + .filter(Boolean) as fb.Lockdate[] + }, [walletInfo.fidelityBondSummary.fbOutputs]) + const availableLockdateOptions = useMemo( + () => lockdateOptions.filter((option) => !existingFbLockdates.includes(option.value)), + [existingFbLockdates, lockdateOptions], + ) const jarIndexes = useMemo(() => walletInfo.jars.map((jar) => jar.jarIndex), [walletInfo.jars]) const formSchema = useMemo( - () => createFidelityBondFormSchema(lockdateOptions, jarIndexes, t), - [lockdateOptions, jarIndexes, t], + () => createFidelityBondFormSchema(availableLockdateOptions, jarIndexes, t), + [availableLockdateOptions, jarIndexes, t], ) const form = useForm({ mode: 'onChange', @@ -55,7 +71,7 @@ export function useCreateFidelityBondWizard( }) const watchedLockdate = useWatch({ control: form.control, name: 'lockdate' }) - const selectedLockdate = watchedLockdate ?? '' + const selectedLockdate: fb.Lockdate | '' = watchedLockdate ?? '' const selectedJarIndex = useWatch({ control: form.control, name: 'source.fromJar' }) const selectedUtxoIds = useWatch({ control: form.control, @@ -68,20 +84,6 @@ export function useCreateFidelityBondWizard( defaultValue: CREATE_FIDELITY_BOND_FORM_DEFAULT_VALUES.confirmationAccepted, }) - const yearOptions = useMemo(() => getYearOptions(lockdateOptions), [lockdateOptions]) - const monthOptions = useMemo(() => getMonthOptions(), []) - - const minLockdate = lockdateOptions.at(0)?.value ?? '' - const maxLockdate = lockdateOptions.at(-1)?.value ?? '' - const clampLockdate = (lockdate: string): fb.Lockdate | '' => { - if (!lockdate || lockdate < minLockdate) return minLockdate || '' - if (lockdate > maxLockdate) return maxLockdate || '' - return lockdate as fb.Lockdate - } - const selectedYear = selectedLockdate ? selectedLockdate.slice(0, 4) : '' - const selectedMonth = selectedLockdate ? selectedLockdate.slice(5, 7) : '' - const minYear = minLockdate ? Number.parseInt(minLockdate.slice(0, 4), 10) : 0 - const minMonth = minLockdate ? Number.parseInt(minLockdate.slice(5, 7), 10) : 1 const setSelectedLockdate = (lockdate: fb.Lockdate | '') => { form.setValue('lockdate', lockdate || undefined, { shouldDirty: true, shouldTouch: true, shouldValidate: true }) } @@ -99,16 +101,6 @@ export function useCreateFidelityBondWizard( }) } - const existingFbLockdates = useMemo(() => { - return walletInfo.fidelityBondSummary.fbOutputs - .filter((fbUtxo) => fb.utxo.isLocked(fbUtxo)) - .map((fbUtxo) => { - const locktime = fb.utxo.getLocktime(fbUtxo) - return locktime ? fb.lockdate.fromTimestamp(locktime) : null - }) - .filter(Boolean) as fb.Lockdate[] - }, [walletInfo.fidelityBondSummary.fbOutputs]) - const hasDuplicateLockdate = selectedLockdate && existingFbLockdates.includes(selectedLockdate) const selectedJar = useMemo(() => { @@ -118,21 +110,19 @@ export function useCreateFidelityBondWizard( const jarsWithUtxos = useMemo(() => { return walletInfo.jars.filter((jar) => { - // TODO: let's allow selecting frozen utxos - unfreeze them before sending the transaction - const availableUtxos = jar.utxos.filter((utxo) => !utxo.frozen && !fb.utxo.isFidelityBond(utxo)) + const availableUtxos = jar.utxos.filter((utxo) => + fb.utxo.isEligibleForCreation(utxo, walletInfo.addressSummary[utxo.address]?.status), + ) return availableUtxos.length > 0 }) - }, [walletInfo.jars]) + }, [walletInfo.addressSummary, walletInfo.jars]) const availableUtxos = useMemo(() => { if (selectedJar === undefined) return [] - return ( - selectedJar.utxos - // TODO: let's allow selecting frozen utxos - unfreeze them before sending the transaction - .filter((utxo) => !utxo.frozen && !fb.utxo.isFidelityBond(utxo)) - .toSorted((a, b) => b.value - a.value) - ) - }, [selectedJar]) + return selectedJar.utxos + .filter((utxo) => fb.utxo.isEligibleForCreation(utxo, walletInfo.addressSummary[utxo.address]?.status)) + .toSorted((a, b) => b.value - a.value) + }, [selectedJar, walletInfo.addressSummary]) const selectedUtxos = useMemo(() => { const availableUtxosById = new Map(availableUtxos.map((utxo) => [utxo.utxo, utxo])) @@ -171,30 +161,6 @@ export function useCreateFidelityBondWizard( const address = timelockAddressQuery.data?.address - const freezeUtxo = useMutation({ - ...freezeMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.error_freezing_utxos')} ${reason}`) - }, - }) - - const unfreezeUtxo = useMutation({ - ...freezeMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.error_unfreezing_utxos')} ${reason}`) - }, - }) - - const directSend = useMutation({ - ...directsendMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.error_creating_fidelity_bond')} ${reason}`) - }, - }) - const handleReset = () => { setStep('select_date') form.reset(CREATE_FIDELITY_BOND_FORM_DEFAULT_VALUES) @@ -311,17 +277,15 @@ export function useCreateFidelityBondWizard( setStep('creating') try { + // TODO: refactor: use the sweep functionality from `useFidelityBondSweep` here const result = await directSend.mutateAsync({ path: { walletname: walletFileName }, body: { mixdepth: selectedJar.jarIndex, - amount_sats: 0, + amount_sats: 0, // 0 := sweep! destination: address, }, }) - setTxResult(result) - setStep('success') - toast.success(t('earn.fidelity_bond.create_fidelity_bond.success_text')) // Best-effort cleanup — tx already broadcast, don't throw on unfreeze failure for (const utxo of frozenUtxos) { @@ -329,12 +293,33 @@ export function useCreateFidelityBondWizard( await unfreezeUtxo.mutateAsync({ path: { walletname: walletFileName }, body: { 'utxo-string': utxo.utxo, freeze: false }, + throwOnError: true, }) - } catch { - // logged via onError + } catch (_ignoredOnPurpose: unknown) { + // only debug output + console.debug('Error while unfreezing previously frozen UTXO.') } } + // freeze resulting fidelity bond output + if (JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS) { + try { + await delayedPromise(JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS_DELAY) + const fbUtxoId = `${result.txinfo.txid}:0` // sent with a sweep, so it is a single output + await freezeUtxo.mutateAsync({ + path: { walletname: walletFileName }, + body: { 'utxo-string': fbUtxoId, freeze: true }, + throwOnError: true, + }) + } catch (_ignoredOnPurpose: unknown) { + console.warn('Error while freezing Fidelity Bond UTXO - continuing.') + } + } + + setTxResult(result) + setStep('success') + toast.success(t('earn.fidelity_bond.create_fidelity_bond.success_text')) + await walletInfo.refetch() } catch { // Best-effort rollback — unfreeze UTXOs that were frozen for this operation @@ -344,8 +329,9 @@ export function useCreateFidelityBondWizard( path: { walletname: walletFileName }, body: { 'utxo-string': utxo.utxo, freeze: false }, }) - } catch { - // logged via onError + } catch (_ignoredOnPurpose: unknown) { + // only debug output + console.debug('Error while unfreezing previously frozen UTXO in error handling.') } } setFrozenUtxos([]) @@ -370,28 +356,14 @@ export function useCreateFidelityBondWizard( } const toggleUtxoSelection = (utxo: Utxo) => { - // TODO: select/deselect all utxos to the same address const isSelected = selectedUtxoIds.includes(utxo.utxo) - setSelectedUtxoIds( - isSelected ? selectedUtxoIds.filter((utxoId) => utxoId !== utxo.utxo) : [...selectedUtxoIds, utxo.utxo], - ) + setSelectedUtxoIds(isSelected ? [] : [utxo.utxo]) } - const selectAllUtxos = () => setSelectedUtxoIds(availableUtxos.map((utxo) => utxo.utxo)) - const deselectAllUtxos = () => setSelectedUtxoIds([]) - - const selectedDateLabel = selectedLockdate - ? new Date(fb.lockdate.toTimestamp(selectedLockdate)).toLocaleDateString(undefined, { - year: 'numeric', - month: 'long', - day: 'numeric', - }) - : null - const canProceed = () => { switch (step) { case 'select_date': - return !!selectedLockdate + return !!selectedLockdate && !hasDuplicateLockdate case 'select_jar': return selectedJar !== undefined case 'select_utxos': @@ -415,15 +387,8 @@ export function useCreateFidelityBondWizard( setStep, setSelectedLockdate, selectedLockdate, - selectedYear, - selectedMonth, - minYear, - minMonth, - yearOptions, - monthOptions, - clampLockdate, hasDuplicateLockdate, - selectedDateLabel, + existingFbLockdates, selectedJarIndex, setSelectedJarIndex, jarsWithUtxos, @@ -432,8 +397,6 @@ export function useCreateFidelityBondWizard( utxoPage, setUtxoPage, toggleUtxoSelection, - selectAllUtxos, - deselectAllUtxos, totalAmount, isUsingAllFunds, utxosToFreeze, diff --git a/src/components/earn/EarnForm.test.tsx b/src/components/earn/EarnForm.test.tsx index f1e5b9c3..ac54bc8c 100644 --- a/src/components/earn/EarnForm.test.tsx +++ b/src/components/earn/EarnForm.test.tsx @@ -9,10 +9,6 @@ vi.mock('react-i18next', () => ({ }), })) -vi.mock('@/components/ui/jam/CurrencySymbol', () => ({ - SatSymbol: (props: Record) => sat-symbol, -})) - vi.mock('../dev/DevBadge', () => ({ DevBadge: ({ className }: { className?: string }) => dev-badge, })) diff --git a/src/components/earn/EarnForm.tsx b/src/components/earn/EarnForm.tsx index e2186199..9a4d7243 100644 --- a/src/components/earn/EarnForm.tsx +++ b/src/components/earn/EarnForm.tsx @@ -3,7 +3,7 @@ import { yupResolver } from '@hookform/resolvers/yup' import type { TFunction } from 'i18next' import { HandshakeIcon, PercentIcon } from 'lucide-react' import { useForm, useWatch } from 'react-hook-form' -import type { Resolver, SubmitHandler } from 'react-hook-form' +import type { SubmitHandler } from 'react-hook-form' import { useTranslation } from 'react-i18next' import * as yup from 'yup' import { Button } from '@/components/ui/button' @@ -17,7 +17,7 @@ import { cn, factorToPercentage, isValidInteger, isValidNumber } from '@/lib/uti import type { AmountSats } from '@/types/global' import { DevBadge } from '../dev/DevBadge' import { Card, CardContent, CardHeader } from '../ui/card' -import { Field, FieldDescription, FieldLabel } from '../ui/field' +import { Field, FieldDescription, FieldError, FieldLabel } from '../ui/field' import { InputGroup, InputGroupAddon, InputGroupInput } from '../ui/input-group' import { Spinner } from '../ui/spinner' @@ -90,8 +90,8 @@ const OfferTypeInput = (props: React.ComponentProps) => { const id = useId() return ( - -
+ +
) => {
-
+
({ mode: 'onSubmit', defaultValues: FORM_INPUT_DEFAULT_VALUES, - // force type (see https://github.com/react-hook-form/resolvers/issues/807) - resolver: yupResolver(schema) as Resolver, + resolver: yupResolver(schema), }) const values = useWatch({ control }) @@ -203,89 +202,81 @@ export function EarnForm({ /> -
- - - {t('earn.label_abs_fee', { - fee: '', // empty on purpose + + + {t('earn.label_abs_fee', { + fee: '', // empty on purpose + })} + + {t('earn.description_abs_fee')} + + - {t('earn.description_abs_fee')} - - - {FieldPrefixSatSymbol} - - - {errors.offerAbsoluteFee?.message && ( -
{errors.offerAbsoluteFee.message}
- )} -
+ type="number" + step={1} + /> + {FieldPrefixSatSymbol} + + + {errors.offerAbsoluteFee?.message && {errors.offerAbsoluteFee.message}} +
-
- - - {t('earn.label_rel_fee', { - fee: getValues('offerRelativeFeeInPercent') ? `(${getValues('offerRelativeFeeInPercent')!}%)` : '', + + + {t('earn.label_rel_fee', { + fee: getValues('offerRelativeFeeInPercent') ? `(${getValues('offerRelativeFeeInPercent')!}%)` : '', + })} + + {t('earn.description_rel_fee')} + + - {t('earn.description_rel_fee')} - - - % - - - {errors.offerRelativeFeeInPercent && ( -
{errors.offerRelativeFeeInPercent.message}
- )} -
+ type="number" + step={factorToPercentage(JAM.OFFER_FEE_REL_STEP)} + /> + % + + {errors.offerRelativeFeeInPercent && {errors.offerRelativeFeeInPercent.message}} +
-
- - {t('earn.label_min_amount_input')} - {t('earn.description_min_amount_input')} - - - {FieldPrefixSatSymbol} - - + + {t('earn.label_min_amount_input')} + {t('earn.description_min_amount_input')} + + + {FieldPrefixSatSymbol} + {errors.offerMinAmount && ( -
+ {offerMinsizeMax < JAM.OFFER_MINSIZE_MIN ? ( <>{t('earn.feedback_invalid_min_amount_insufficient_funds')} ) : ( <>{errors.offerMinAmount?.message || t('earn.feedback_invalid_min_amount')} )} -
+ )} -
+ + -
- {feeConfigValidation.maxFeesConfigMissing && ( setShowFeeConfigDialog(true)} className="mb-4" /> )} @@ -236,7 +242,7 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => { {jmSession.coinjoin_in_process === true && ( - {t('send.text_coinjoin_already_running')} + {t('send.text_coinjoin_already_running')} )} @@ -249,13 +255,13 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => { {isWaitingMakerStart && ( - {/* TODO: i18n */ t('Waiting for maker to start...')} + {t('earn.alert_waiting_start_title')} )} {isWaitingMakerStop && ( - {/* TODO: i18n */ t('Waiting for maker to stop...')} + {t('earn.alert_waiting_stop_title')} )} {waitingForOfferUpdate && ( @@ -265,11 +271,33 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => { )} + {!isWaitingMakerStart && !isWaitingMakerStop && !jmSession.maker_running && earnPreconditionWarning && ( + <> + {earnPreconditionWarning === 'non-frozen-fb-present' ? ( + + ) : null} + {earnPreconditionWarning === 'missing-utxos' ? ( + + ) : null} + {earnPreconditionWarning === 'missing-confirmations' ? : null} + + )} + +
+ +
+ {jmSession.offer_list && jmSession.offer_list.length > 0 && (
) : ( - <> +
{/* Fidelity Bonds exist */}

{t('earn.title_fidelity_bonds', { count: walletInfo.fidelityBondSummary.fbOutputs.length })}

{t('earn.subtitle_fidelity_bonds')}

-
+
{walletInfo.fidelityBondSummary.fbOutputs.map((it) => { const isExpired = !fb.utxo.isLocked(it) - const actionsEnabled = - isExpired && - !jmSession.rescanning && - !jmSession.maker_running && - !waitingForMakerUpdate && - !waitingForOfferUpdate && - !walletInfo.isFetching + const actionsEnabled = isExpired && isFidelityBondActionsEnabled return ( - {actionsEnabled && ( -
- - @@ -373,7 +428,21 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => { ) })}
- + + {showCreateAdditionalFidelityBond && ( +
+ + +
+ )} +
)} {/* Fee Configuration Dialog */} @@ -418,11 +487,11 @@ export const EarnPage = ({ walletFileName }: EarnPageProps) => {
- walletInfo.fidelityBondSummary.fbOutputs: + walletInfo.fidelityBondSummary.fbOutputs:
{JSON.stringify(walletInfo.fidelityBondSummary.fbOutputs, null, 2)}
- jmSessionState.offer_list: + jmSessionState.offer_list:
{JSON.stringify(jmSession.offer_list, null, 2)}
diff --git a/src/components/earn/EarnPreconditionAlerts.tsx b/src/components/earn/EarnPreconditionAlerts.tsx new file mode 100644 index 00000000..a95b1afe --- /dev/null +++ b/src/components/earn/EarnPreconditionAlerts.tsx @@ -0,0 +1,77 @@ +import { AlertTriangleIcon } from 'lucide-react' +import { Trans, useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +import { routes } from '@/constants/routes' + +export const UnconfirmedBalanceAlert = ({ numberOfMissingConfirmations }: { numberOfMissingConfirmations: number }) => { + const { t } = useTranslation() + + return ( + + + {t('earn.precondition.title')} + +
    +
  • + {t('earn.precondition.hint_missing_confirmations', { + count: numberOfMissingConfirmations, + })} +
  • +
+
+
+ ) +} + +export const NoSpendableBalanceAlert = () => { + const { t } = useTranslation() + + return ( + + + {t('earn.precondition.title')} + +
    +
  • + + + Fund your wallet + + to start earning. + +
  • +
+
+
+ ) +} + +export const NonFrozenFidelityBondsAlert = ({ + numberOfNonFrozenFidelityBondOutputs, +}: { + numberOfNonFrozenFidelityBondOutputs: number +}) => { + const { t } = useTranslation() + + return ( + + + {t('earn.precondition.title')} + + +
    +
  • + , + }} + /> +
  • +
+
+
+ ) +} diff --git a/src/components/earn/FidelityBondCard.test.tsx b/src/components/earn/FidelityBondCard.test.tsx index 60b17a50..b7cffbfc 100644 --- a/src/components/earn/FidelityBondCard.test.tsx +++ b/src/components/earn/FidelityBondCard.test.tsx @@ -34,7 +34,7 @@ vi.mock('../ui/jam/Address', () => ({ Address: ({ value }: { value?: string }) => {value}, })) -vi.mock('../ui/jam/Balance', () => ({ +vi.mock('@/components/ui/jam/Balance', () => ({ Balance: ({ valueString }: { valueString?: string }) => {valueString}, })) diff --git a/src/components/earn/FidelityBondCard.tsx b/src/components/earn/FidelityBondCard.tsx index 0a4c108a..ef254522 100644 --- a/src/components/earn/FidelityBondCard.tsx +++ b/src/components/earn/FidelityBondCard.tsx @@ -2,18 +2,19 @@ import { useMemo, type PropsWithChildren } from 'react' import { ClockIcon, CoinsIcon, CopyIcon } from 'lucide-react' import { Trans, useTranslation } from 'react-i18next' import { Card, CardContent, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' +import { Balance } from '@/components/ui/jam/Balance' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import type { FidelityBondUtxo } from '@/hooks/useQueryUtxos' import * as fb from '@/lib/fidelityBondUtils' -import { time } from '@/lib/utils' +import { cn, time } from '@/lib/utils' import { Address } from '../ui/jam/Address' -import { Balance } from '../ui/jam/Balance' interface FidelityBondCardProps { value: FidelityBondUtxo + className?: string } -export function FidelityBondCard({ value, children }: PropsWithChildren) { +export function FidelityBondCard({ value, className, children }: PropsWithChildren) { const { t, i18n } = useTranslation() const isExpired = !fb.utxo.isLocked(value) @@ -31,7 +32,7 @@ export function FidelityBondCard({ value, children }: PropsWithChildren + {isExpired ? ( @@ -42,7 +43,7 @@ export function FidelityBondCard({ value, children }: PropsWithChildren - + {value.path} @@ -54,29 +55,29 @@ export function FidelityBondCard({ value, children }: PropsWithChildren -
- -
+
+ +
-
- -
+
+ +
{t(`earn.fidelity_bond.existing.${isExpired ? 'label_expired_on' : 'label_locked_until'}`)} - + {value.locktime} ({humanReadableLockDuration})
-
- -
+
+ +
{t('earn.fidelity_bond.existing.label_address')} diff --git a/src/components/earn/MoveToJarDialog.schema.test.ts b/src/components/earn/MoveToJarDialog.schema.test.ts new file mode 100644 index 00000000..efa65a6a --- /dev/null +++ b/src/components/earn/MoveToJarDialog.schema.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest' +import { createMoveToJarFormSchema } from './MoveToJarDialog.schema' + +describe('createMoveToJarFormSchema', () => { + const schema = createMoveToJarFormSchema([0, 2]) + + it('accepts an available destination jar', async () => { + await expect(schema.validate({ destinationJarIndex: 2 })).resolves.toEqual({ destinationJarIndex: 2 }) + }) + + it.each([{ destinationJarIndex: 1 }, { destinationJarIndex: undefined }])( + 'rejects an unavailable destination jar', + async (values) => { + await expect(schema.isValid(values)).resolves.toBe(false) + }, + ) +}) diff --git a/src/components/earn/MoveToJarDialog.schema.ts b/src/components/earn/MoveToJarDialog.schema.ts new file mode 100644 index 00000000..a14aa72a --- /dev/null +++ b/src/components/earn/MoveToJarDialog.schema.ts @@ -0,0 +1,13 @@ +import * as yup from 'yup' +import type { JarIndex } from '@/types/global' + +export type MoveToJarFormValues = { + destinationJarIndex: JarIndex +} + +export const createMoveToJarFormSchema = (availableJarIndexes: JarIndex[]): yup.ObjectSchema => + yup + .object({ + destinationJarIndex: yup.number().oneOf(availableJarIndexes).required(), + }) + .required() diff --git a/src/components/earn/MoveToJarDialog.test.tsx b/src/components/earn/MoveToJarDialog.test.tsx index 6f03b3c4..d9a23d55 100644 --- a/src/components/earn/MoveToJarDialog.test.tsx +++ b/src/components/earn/MoveToJarDialog.test.tsx @@ -4,17 +4,20 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' import type { FidelityBondUtxo } from '@/hooks/useQueryUtxos' import { MoveToJarDialog } from './MoveToJarDialog' +type MutationOptions = { mutationFn: (input: unknown) => Promise } + type ChildrenProps = { children: ReactNode } type DialogProps = ChildrenProps & { open?: boolean; onOpenChange?: (open: boolean) => void } type ValueProps = { value: string } const h = vi.hoisted(() => ({ - queryReturn: { + fetchAddressQueryData: { data: undefined as { address?: string } | undefined, isLoading: false, isError: false, }, - mutateAsync: vi.fn<() => Promise>().mockResolvedValue({ txinfo: { txid: 'movetxid99' } }), + directSendAsync: vi.fn<() => Promise>().mockResolvedValue({ txinfo: { txid: 'movetxid99' } }), + freezeAsync: vi.fn<() => Promise>().mockResolvedValue({}), isPending: false, refetch: vi.fn(), extraUtxos: [] as Array<{ utxo: string; value: number; frozen: boolean }>, @@ -27,13 +30,16 @@ vi.mock('react-i18next', () => ({ })) vi.mock('@tanstack/react-query', () => ({ - useQuery: () => h.queryReturn, - useMutation: () => ({ mutateAsync: h.mutateAsync, isPending: h.isPending }), + useQuery: () => h.fetchAddressQueryData, + useMutation: vi.fn((options: MutationOptions) => ({ + isPending: false, + mutateAsync: async (input: unknown) => await options.mutationFn(input), + })), })) -vi.mock('@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query', () => ({ - directsendMutation: vi.fn(() => ({})), - freezeMutation: vi.fn(() => ({})), +vi.mock('@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query', () => ({ + directsendMutation: vi.fn(() => ({ mutationFn: h.directSendAsync })), + freezeMutation: vi.fn(() => ({ mutationFn: h.freezeAsync })), getaddressOptions: vi.fn(() => ({ queryKey: ['mock'], queryFn: vi.fn() })), })) @@ -64,10 +70,29 @@ vi.mock('@/context/JamWalletInfoContext', () => ({ utxos: [], }, ], + walletBalanceSummary: { calculatedTotalBalanceInSats: 6000 }, refetch: h.refetch, }), })) +vi.mock('@/components/ui/jam/SelectableJar', () => ({ + SelectableJar: ({ + name, + onSelect, + disabled, + isSelected, + }: { + name?: string + onSelect?: () => void + disabled?: boolean + isSelected?: boolean + }) => ( + + ), +})) + vi.mock('@/components/ui/dialog', () => ({ Dialog: ({ children, open, onOpenChange }: DialogProps) => open ? ( @@ -93,6 +118,10 @@ vi.mock('@/components/ui/jam/Address', () => ({ Address: ({ value }: ValueProps) =>
{value}
, })) +vi.mock('@/components/ui/jam/Balance', () => ({ + Balance: ({ valueString }: { valueString: string }) => {valueString} sats, +})) + vi.mock('@/components/ui/jam/CopyButton', () => ({ CopyButton: ({ value }: ValueProps) =>
{value}
, })) @@ -118,13 +147,14 @@ const renderDialog = (props?: { utxo?: FidelityBondUtxo; onOpenChange?: (open: b const goToConfirm = () => { fireEvent.click(screen.getByText('Jar 1')) - fireEvent.click(screen.getByText('earn.fidelity_bond.select_date.text_primary_button')) + fireEvent.click(screen.getByText('global.next')) } describe('MoveToJarDialog', () => { beforeEach(() => { - h.queryReturn = { data: { address: 'bc111' }, isLoading: false, isError: false } - h.mutateAsync = vi.fn<() => Promise>().mockResolvedValue({ txinfo: { txid: 'movetxid99' } }) + h.fetchAddressQueryData = { data: { address: 'bc111' }, isLoading: false, isError: false } + h.directSendAsync = vi.fn<() => Promise>().mockResolvedValue({ txinfo: { txid: 'movetxid99' } }) + h.freezeAsync = vi.fn<() => Promise>().mockResolvedValue({}) h.isPending = false h.refetch = vi.fn() h.extraUtxos = [] @@ -134,35 +164,35 @@ describe('MoveToJarDialog', () => { renderDialog() expect(screen.getByText('earn.fidelity_bond.move.title')).toBeInTheDocument() expect(screen.getByText('earn.fidelity_bond.move.select_jar.description')).toBeInTheDocument() + expect(screen.getByText('Jar 0')).toBeEnabled() expect(screen.getByText('Jar 1')).toBeInTheDocument() }) it('moves to the confirm step and shows the destination address', () => { renderDialog() goToConfirm() - expect(screen.getByText('earn.fidelity_bond.review_inputs.label_jar_n {"jar":1}')).toBeInTheDocument() + expect(screen.getByText('Jar 1')).toBeInTheDocument() expect(screen.getByTestId('address')).toHaveTextContent('bc111') }) it('shows a loading indicator while the destination address loads', () => { - h.queryReturn = { data: undefined, isLoading: true, isError: false } + h.fetchAddressQueryData = { data: undefined, isLoading: true, isError: false } renderDialog() goToConfirm() expect(screen.getByText('earn.fidelity_bond.move.text_loading')).toBeInTheDocument() }) it('shows an error alert when the address query fails', () => { - h.queryReturn = { data: undefined, isLoading: false, isError: true } + h.fetchAddressQueryData = { data: undefined, isLoading: false, isError: true } renderDialog() goToConfirm() - expect(screen.getByText('earn.fidelity_bond.move.error_loading_address')).toBeInTheDocument() + expect(screen.getByText('global.errors.error_loading_address_failed')).toBeInTheDocument() }) it('completes the move flow and reaches the success step', async () => { h.extraUtxos = [{ utxo: 'tx2:0', value: 500, frozen: false }] renderDialog() goToConfirm() - fireEvent.click(screen.getByTestId('confirm-switch')) fireEvent.click(screen.getByText('earn.fidelity_bond.move.text_button_submit')) await waitFor(() => expect(screen.getByText('earn.fidelity_bond.move.success_text')).toBeInTheDocument()) @@ -174,19 +204,17 @@ describe('MoveToJarDialog', () => { const frozenUtxo = { ...utxo, frozen: true } as unknown as FidelityBondUtxo renderDialog({ utxo: frozenUtxo }) goToConfirm() - fireEvent.click(screen.getByTestId('confirm-switch')) fireEvent.click(screen.getByText('earn.fidelity_bond.move.text_button_submit')) await waitFor(() => expect(screen.getByText('earn.fidelity_bond.move.success_text')).toBeInTheDocument()) - const calls = h.mutateAsync.mock.calls as unknown as Array<[{ body?: { freeze?: boolean } }]> + const calls = h.freezeAsync.mock.calls as unknown as Array<[{ body?: { freeze?: boolean } }]> expect(calls.some((call) => call[0]?.body?.freeze === false)).toBe(true) }) it('returns to the confirm step when sending fails', async () => { - h.mutateAsync = vi.fn<() => Promise>().mockRejectedValue(new Error('send failed')) + h.directSendAsync = vi.fn<() => Promise>().mockRejectedValue(new Error('send failed')) renderDialog() goToConfirm() - fireEvent.click(screen.getByTestId('confirm-switch')) fireEvent.click(screen.getByText('earn.fidelity_bond.move.text_button_submit')) await waitFor(() => diff --git a/src/components/earn/MoveToJarDialog.tsx b/src/components/earn/MoveToJarDialog.tsx index 09e6d98f..e6d714d4 100644 --- a/src/components/earn/MoveToJarDialog.tsx +++ b/src/components/earn/MoveToJarDialog.tsx @@ -1,48 +1,39 @@ -import { useState, useMemo } from 'react' -import { - directsendMutation, - freezeMutation, - getaddressOptions, -} from '@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query' -import type { DirectSendResponse, ErrorMessage } from '@joinmarket-webui/joinmarket-api-ts/jm' -import { useMutation, useQuery } from '@tanstack/react-query' -import { - AlertTriangleIcon, - CheckCircle2Icon, - ChevronLeftIcon, - CopyIcon, - CheckIcon, - UnlockIcon, - Loader2Icon, -} from 'lucide-react' +import { useMemo, useState } from 'react' +import { yupResolver } from '@hookform/resolvers/yup' +import { getaddressOptions } from '@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query' +import type { DirectSendResponse } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' +import { useQuery } from '@tanstack/react-query' +import { AlertTriangleIcon, UnlockIcon } from 'lucide-react' +import { useForm, useWatch } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' -import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +import { Alert, AlertTitle } from '@/components/ui/alert' import { Button } from '@/components/ui/button' -import { buttonVariants } from '@/components/ui/button-variants' -import { Card, CardContent } from '@/components/ui/card' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog' -import { CopyButton } from '@/components/ui/jam/CopyButton' -import { Label } from '@/components/ui/label' -import { Spinner } from '@/components/ui/spinner' -import { Switch } from '@/components/ui/switch' import { useJamWalletInfoContext } from '@/context/JamWalletInfoContext' import { useApiClient } from '@/hooks/useApiClient' -import type { FidelityBondUtxo, Utxo } from '@/hooks/useQueryUtxos' -import { getErrorReason } from '@/lib/errorReason' -import { cn, formatSats, type WalletFileName } from '@/lib/utils' -import type { JarIndex } from '@/types/global' -import { Address } from '../ui/jam/Address' +import type { FidelityBondUtxo } from '@/hooks/useQueryUtxos' +import type { WalletFileName } from '@/lib/utils' +import { createMoveToJarFormSchema, type MoveToJarFormValues } from './MoveToJarDialog.schema' +import { FidelityBondDialogLayout } from './fidelity-bond/FidelityBondDialogLayout' +import { + AddressPreview, + CopyableField, + FidelityBondAmount, + InfoCard, + InlineLoading, + JarBadge, + PendingStep, + StepIntro, + SuccessHeading, +} from './fidelity-bond/FidelityBondDialogParts' +import { FidelityBondJarSelector } from './fidelity-bond/FidelityBondJarSelector' +import { WizardStepFooter } from './fidelity-bond/WizardStepFooter' +import { useFidelityBondSweep } from './fidelity-bond/useFidelityBondSweep' type Step = 'select_jar' | 'confirm' | 'sending' | 'success' +const WIZARD_STEPS: Step[] = ['select_jar', 'confirm'] + interface MoveToJarDialogProps { open: boolean onOpenChange: (open: boolean) => void @@ -56,23 +47,33 @@ export function MoveToJarDialog({ open, onOpenChange, walletFileName, utxo }: Mo const walletInfo = useJamWalletInfoContext() const [step, setStep] = useState('select_jar') - const [selectedJarIndex, setSelectedJarIndex] = useState() - const [confirmationChecked, setConfirmationChecked] = useState(false) const [txResult, setTxResult] = useState() - const [error, setError] = useState() - const sourceJar = walletInfo.jars.find((jar) => jar.jarIndex === utxo.mixdepth) + const availableJarIndexes = useMemo(() => walletInfo.jars.map((jar) => jar.jarIndex), [walletInfo.jars]) + const formSchema = useMemo(() => createMoveToJarFormSchema(availableJarIndexes), [availableJarIndexes]) + const { + control, + handleSubmit, + reset, + setValue, + formState: { isSubmitting }, + } = useForm({ + mode: 'onChange', + defaultValues: { + destinationJarIndex: undefined, + }, + resolver: yupResolver(formSchema), + }) + const selectedJarIndex = useWatch({ control, name: 'destinationJarIndex' }) - // All jars except the FB's source jar are valid destinations - const destinationJars = useMemo(() => { - return walletInfo.jars.filter((jar) => jar.jarIndex !== utxo.mixdepth) - }, [walletInfo.jars, utxo.mixdepth]) + const { sweep, isLoading, error, setError, sourceJar } = useFidelityBondSweep({ + walletFileName, + utxo, + unfreezeErrorKey: 'earn.fidelity_bond.move.error_unfreezing_fidelity_bond', + sendErrorKey: 'earn.fidelity_bond.move.error_spending_fidelity_bond', + }) - // UTXOs in the source jar that are NOT this FB — they need to be frozen during sweep - const utxosToFreeze = useMemo(() => { - if (!sourceJar) return [] - return sourceJar.utxos.filter((u) => u.utxo !== utxo.utxo && !u.frozen) - }, [sourceJar, utxo.utxo]) + const destinationJar = walletInfo.jars.find((jar) => jar.jarIndex === selectedJarIndex) const getAddressQueryOptions = getaddressOptions({ client, @@ -89,40 +90,12 @@ export function MoveToJarDialog({ open, onOpenChange, walletFileName, utxo }: Mo retry: false, }) - if (getAddressQuery.isError && !error) { - setError(t('earn.fidelity_bond.move.error_loading_address')) - } - const destinationAddress = getAddressQuery.data?.address - - const freezeUtxo = useMutation({ - ...freezeMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.move.error_freezing_utxos')} ${reason}`) - }, - }) - - const unfreezeUtxo = useMutation({ - ...freezeMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.move.error_unfreezing_fidelity_bond')} ${reason}`) - }, - }) - - const directSend = useMutation({ - ...directsendMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.move.error_spending_fidelity_bond')} ${reason}`) - }, - }) + const displayError = error ?? (getAddressQuery.isError ? t('global.errors.error_loading_address_failed') : undefined) const handleReset = () => { setStep('select_jar') - setSelectedJarIndex(undefined) - setConfirmationChecked(false) + reset() setTxResult(undefined) setError(undefined) } @@ -134,293 +107,131 @@ export function MoveToJarDialog({ open, onOpenChange, walletFileName, utxo }: Mo onOpenChange(newOpen) } - const handleSubmit = async () => { - if (selectedJarIndex === undefined || !destinationAddress) return + const submitMove = handleSubmit(async () => { + if (!destinationAddress) return setStep('sending') - setError(undefined) + const txResult = await sweep({ + destination: destinationAddress, + tryFreezeAfterBroadcast: false, + }) - const frozen: Utxo[] = [] - try { - // Freeze other UTXOs in the source jar so only the FB gets swept - for (const u of utxosToFreeze) { - await freezeUtxo.mutateAsync({ - path: { walletname: walletFileName }, - body: { 'utxo-string': u.utxo, freeze: true }, - }) - frozen.push(u) - } + setTxResult(txResult) - if (utxo.frozen) { - await unfreezeUtxo.mutateAsync({ - path: { walletname: walletFileName }, - body: { 'utxo-string': utxo.utxo, freeze: false }, - }) - } - - const result = await directSend.mutateAsync({ - path: { walletname: walletFileName }, - body: { - mixdepth: utxo.mixdepth, - amount_sats: 0, - destination: destinationAddress, - }, - }) - - setTxResult(result) + if (txResult) { setStep('success') toast.success(t('earn.fidelity_bond.move.success_text')) - - // Best-effort cleanup — tx already broadcast, don't throw on unfreeze failure - for (const u of frozen) { - try { - await unfreezeUtxo.mutateAsync({ - path: { walletname: walletFileName }, - body: { 'utxo-string': u.utxo, freeze: false }, - }) - } catch { - // logged via onError - } - } - - await walletInfo.refetch() - } catch { - // Best-effort rollback — unfreeze UTXOs that were frozen before the error - for (const u of frozen) { - try { - await unfreezeUtxo.mutateAsync({ - path: { walletname: walletFileName }, - body: { 'utxo-string': u.utxo, freeze: false }, - }) - } catch { - // logged via onError - } - } + } else { setStep('confirm') } + }) + + const renderFooter = () => { + switch (step) { + case 'select_jar': + return ( + handleOpenChange(false)} + onPrimary={() => setStep('confirm')} + primaryDisabled={selectedJarIndex === undefined} + primaryLabel={t('global.next')} + /> + ) + case 'confirm': + return ( + setStep('select_jar')} + onCancel={() => handleOpenChange(false)} + onPrimary={() => void submitMove()} + primaryDisabled={!destinationAddress} + isLoading={isLoading || isSubmitting} + primaryLabel={t('earn.fidelity_bond.move.text_button_submit')} + /> + ) + case 'sending': + return null + case 'success': + return + } } - const isLoading = freezeUtxo.isPending || unfreezeUtxo.isPending || directSend.isPending - return ( - - - - {t('earn.fidelity_bond.move.title')} - {t('earn.fidelity_bond.subtitle')} - + + {step === 'select_jar' && ( +
+ - {error && ( - + + + + + + setValue('destinationJarIndex', jarIndex, { + shouldDirty: true, + shouldTouch: true, + shouldValidate: true, + }) + } + isJarDisabled={() => false} + /> +
+ )} + + {step === 'confirm' && ( +
+
+ + + + + + +
+ + + + + + {getAddressQuery.isLoading ? ( + + ) : ( + destinationAddress && ( + + ) + )} + + - {error} + {t('earn.fidelity_bond.move.confirm_send_modal.title')} - )} +
+ )} -
- {step === 'select_jar' && ( -
-
-
- -
-
-

{t('earn.fidelity_bond.move.select_jar.description')}

-
-
+ {step === 'sending' && } -
-

- {t('earn.fidelity_bond.review_inputs.label_amount')} -

-

{formatSats(utxo.value)}

-
+ {step === 'success' && ( +
+ -
- {destinationJars.map((jar) => { - const isSelected = selectedJarIndex === jar.jarIndex - return ( - setSelectedJarIndex(jar.jarIndex)} - > - -
-
-
-

{jar.name}

-
-
-
-

- {formatSats(jar.balanceSummary.calculatedAvailableBalanceInSats)} -

- {isSelected && } -
- - - ) - })} -
-
- )} - - {step === 'confirm' && ( -
-
-
-

- {t('earn.fidelity_bond.review_inputs.label_jar')} -

-

- {t('earn.fidelity_bond.review_inputs.label_jar_n', { jar: utxo.mixdepth })} -

-
-
-

{t('earn.fidelity_bond.move.label_destination')}

-

- {t('earn.fidelity_bond.review_inputs.label_jar_n', { jar: selectedJarIndex })} -

-
-
- -
-

- {t('earn.fidelity_bond.review_inputs.label_amount')} -

-

{formatSats(utxo.value)}

-
- - {getAddressQuery.isLoading ? ( -
- -

{t('earn.fidelity_bond.move.text_loading')}

-
- ) : ( - destinationAddress && ( -
- -
-
-
-
- ) - )} - - - - {t('earn.fidelity_bond.move.confirm_send_modal.title')} - - -
- setConfirmationChecked(checked)} - /> -
- -
-
-
- )} - - {step === 'sending' && ( -
-
- -
- -
-
-

{t('earn.fidelity_bond.move.text_sending')}

-
- )} - - {step === 'success' && ( -
-
-
- -
-

{t('earn.fidelity_bond.move.success_text')}

-
- - {txResult?.txinfo?.txid && ( -
- -
- - {txResult.txinfo.txid} - - } - successText={} - className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'h-10 w-10 shrink-0')} - onSuccess={() => - toast.success(t('earn.fidelity_bond.create_fidelity_bond.text_copy_transaction_id')) - } - onError={() => toast.error(t('global.errors.reason_unknown'))} - /> -
-
- )} -
+ {txResult?.txinfo?.txid && ( + )}
- - {step === 'select_jar' && ( - - - - - )} - - {step === 'confirm' && ( - - - - - - )} - - {step === 'success' && ( - - - - )} - -
+ )} + ) } diff --git a/src/components/earn/OfferCard.test.tsx b/src/components/earn/OfferCard.test.tsx index 9b8722ad..150e93c9 100644 --- a/src/components/earn/OfferCard.test.tsx +++ b/src/components/earn/OfferCard.test.tsx @@ -1,6 +1,7 @@ -import type { SessionResponse } from '@joinmarket-webui/joinmarket-api-ts/jm' +import type { SessionResponse } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' import { render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' +import { withRuntimeLocale } from '@/test/withRuntimeLocale' import { OfferCard } from './OfferCard' type Offer = NonNullable[number] @@ -18,7 +19,7 @@ vi.mock('@/lib/utils', () => ({ isRelativeOffer: (type: string) => type === 'sw0relo', })) -vi.mock('../ui/jam/Balance', () => ({ +vi.mock('@/components/ui/jam/Balance', () => ({ Balance: ({ valueString }: { valueString?: string }) => {valueString}, })) @@ -64,7 +65,7 @@ describe('OfferCard', () => { }) it('renders without txfee', () => { - const offer = { ...baseOffer, ordertype: 'sw0abso', txfee: undefined } + const offer = { ...baseOffer, ordertype: 'sw0abso', txfee: undefined } as unknown as Offer render() expect(screen.queryByText('earn.current.text_txfee')).not.toBeInTheDocument() @@ -79,4 +80,41 @@ describe('OfferCard', () => { expect(screen.getByTestId('child')).toBeInTheDocument() }) + + it('shows whether the offer is visible in the local orderbook', () => { + const { rerender } = render() + + expect(screen.getByText('earn.current.text_orderbook_visible')).toBeInTheDocument() + + rerender() + expect(screen.getByText('earn.current.text_orderbook_missing')).toBeInTheDocument() + }) + + it('shows the advertised fidelity bond details', () => { + withRuntimeLocale('de-DE', () => { + render( + , + ) + + expect(screen.getByText('earn.current.text_fidelity_bond')).toBeInTheDocument() + expect(screen.getByText('earn.current.text_bond_value: 42.000')).toBeInTheDocument() + expect(screen.getByText('100000')).toBeInTheDocument() + expect(screen.getByText(/earn\.current\.text_bond_locktime/u)).toBeInTheDocument() + }) + }) }) diff --git a/src/components/earn/OfferCard.tsx b/src/components/earn/OfferCard.tsx index e42d5d28..9880cdaa 100644 --- a/src/components/earn/OfferCard.tsx +++ b/src/components/earn/OfferCard.tsx @@ -1,11 +1,22 @@ import type { PropsWithChildren } from 'react' -import type { SessionResponse } from '@joinmarket-webui/joinmarket-api-ts/jm' +import type { SessionResponse } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' import type { TFunction } from 'i18next' -import { FingerprintIcon, HandCoinsIcon, Maximize2Icon, Minimize2Icon } from 'lucide-react' +import { + CircleAlertIcon, + CircleCheckIcon, + FingerprintIcon, + HandCoinsIcon, + Maximize2Icon, + Minimize2Icon, + PickaxeIcon, + SearchIcon, + ShieldCheckIcon, +} from 'lucide-react' import { useTranslation } from 'react-i18next' import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import type { OrderbookFidelityBond, OrderbookOffer } from '@/lib/api/orderbook' import { cn, factorToPercentage, isAbsoluteOffer, isRelativeOffer } from '@/lib/utils' import { Balance } from '../ui/jam/Balance' import { Label } from '../ui/label' @@ -15,33 +26,71 @@ type Offer = NonNullable[number] const OfferTypeBadge = ({ value }: { value: Offer }) => { const { t } = useTranslation() const text = renderOfferText(value, t) - return {text} + return ( + + {text} + + ) } const renderOfferText = (value: Offer, t: TFunction<'translation', undefined>) => { - if (isAbsoluteOffer(value?.ordertype || '')) { + if (isAbsoluteOffer(String(value?.ordertype || ''))) { return t('earn.current.text_offer_type_absolute') } - if (isRelativeOffer(value?.ordertype || '')) { + if (isRelativeOffer(String(value?.ordertype || ''))) { return t('earn.current.text_offer_type_relative') } - return value?.ordertype + return String(value?.ordertype || '') } interface OfferCardProps { className?: string value: Offer nickname: SessionResponse['nickname'] + orderbookStatus?: 'checking' | 'visible' | 'missing' | 'error' + orderbookOffer?: OrderbookOffer + fidelityBond?: OrderbookFidelityBond } -export function OfferCard({ className, value, nickname, children }: PropsWithChildren) { +const orderbookStatusEntry = (value: OfferCardProps['orderbookStatus'], t: TFunction) => { + return value === 'visible' + ? { icon: CircleCheckIcon, text: t('earn.current.text_orderbook_visible'), variant: 'success' as const } + : value === 'missing' + ? { icon: SearchIcon, text: t('earn.current.text_orderbook_missing'), variant: 'warning' as const } + : value === 'error' + ? { icon: CircleAlertIcon, text: t('earn.current.text_orderbook_error'), variant: 'destructive' as const } + : value === 'checking' + ? { icon: SearchIcon, text: t('earn.current.text_orderbook_checking'), variant: 'muted' as const } + : undefined +} + +export function OfferCard({ + className, + value, + nickname, + orderbookStatus, + orderbookOffer, + fidelityBond, + children, +}: PropsWithChildren) { const { t } = useTranslation() + const bondValue = Number(orderbookOffer?.fidelity_bond_value) || 0 + + const offerInOrderbookStatus = orderbookStatusEntry(orderbookStatus, t) + const bondWarning = bondValue === 0 || orderbookOffer?.fidelity_bond_verification_stale === true return ( - + {t('earn.current.text_offer')} - + + {offerInOrderbookStatus && ( + + + {offerInOrderbookStatus.text} + + )} + @@ -52,41 +101,43 @@ export function OfferCard({ className, value, nickname, children }: PropsWithChi -
- -
+
+ +
- + {nickname}:{value?.oid}
-
- -
+
+ +
- {isRelativeOffer(value?.ordertype || '') ? ( - {factorToPercentage(Number.parseFloat(value?.cjfee || '') || 0)}% + {isRelativeOffer(String(value?.ordertype || '')) ? ( + + {factorToPercentage(Number.parseFloat(String(value?.cjfee || '')) || 0)}% + ) : ( )}
-
- -
+
+ +
-
- -
+
+ +
@@ -94,8 +145,9 @@ export function OfferCard({ className, value, nickname, children }: PropsWithChi
{!!value?.txfee && ( -
-
+
+ +
@@ -103,6 +155,45 @@ export function OfferCard({ className, value, nickname, children }: PropsWithChi
)} + {fidelityBond !== undefined && ( +
+ 0, + 'text-brand-warning': bondWarning, + })} + /> +
+ +
+
+ + {t('earn.current.text_bond_value')}: {Math.floor(bondValue).toLocaleString()} + +
+
+ + + {t('earn.current.text_bond_locktime', { + date: new Date(fidelityBond.locktime * 1_000).toLocaleDateString(undefined, { + year: 'numeric', + month: 'long', + day: 'numeric', + }), + })} + +
+
+
+
+ )} {children} diff --git a/src/components/earn/RenewBondDialog.schema.test.ts b/src/components/earn/RenewBondDialog.schema.test.ts new file mode 100644 index 00000000..2053bb0a --- /dev/null +++ b/src/components/earn/RenewBondDialog.schema.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest' +import { renewBondFormSchema } from './RenewBondDialog.schema' + +describe('renewBondFormSchema', () => { + it('accepts a lockdate after confirmation', async () => { + const values = { lockdate: '2030-01' as const, confirmationAccepted: true } + await expect(renewBondFormSchema.validate(values)).resolves.toEqual(values) + }) + + it.each([ + { lockdate: undefined, confirmationAccepted: true }, + { lockdate: '2030-01' as const, confirmationAccepted: false }, + ])('rejects incomplete renewal values', async (values) => { + await expect(renewBondFormSchema.isValid(values)).resolves.toBe(false) + }) +}) diff --git a/src/components/earn/RenewBondDialog.schema.ts b/src/components/earn/RenewBondDialog.schema.ts new file mode 100644 index 00000000..a36649f9 --- /dev/null +++ b/src/components/earn/RenewBondDialog.schema.ts @@ -0,0 +1,19 @@ +import * as yup from 'yup' +import * as fb from '@/lib/fidelityBondUtils' + +export type RenewBondFormValues = { + lockdate?: fb.Lockdate + confirmationAccepted: boolean +} + +export const RENEW_BOND_FORM_DEFAULT_VALUES: RenewBondFormValues = { + lockdate: undefined, + confirmationAccepted: false, +} + +export const renewBondFormSchema: yup.ObjectSchema = yup + .object({ + lockdate: yup.string().required(), + confirmationAccepted: yup.boolean().oneOf([true]).required(), + }) + .required() diff --git a/src/components/earn/RenewBondDialog.test.tsx b/src/components/earn/RenewBondDialog.test.tsx index 0fc68cfa..1b181058 100644 --- a/src/components/earn/RenewBondDialog.test.tsx +++ b/src/components/earn/RenewBondDialog.test.tsx @@ -4,13 +4,15 @@ import { describe, expect, it, vi, beforeEach } from 'vitest' import type { FidelityBondUtxo } from '@/hooks/useQueryUtxos' import { RenewBondDialog } from './RenewBondDialog' +type MutationOptions = { mutationFn: (input: unknown) => Promise } + type ChildrenProps = { children?: ReactNode } type ValueProps = { value: string } const h = vi.hoisted(() => ({ - queryReturn: { data: undefined as { address: string } | undefined, isLoading: false, isError: false }, - mutateAsync: vi.fn<() => Promise>().mockResolvedValue({ txinfo: { txid: 'abcd1234' } }), - isPending: false, + fetchAddressQueryData: { data: undefined as { address: string } | undefined, isLoading: false, isError: false }, + directSendAsync: vi.fn<() => Promise>().mockResolvedValue({ txinfo: { txid: 'abcd1234' } }), + freezeAsync: vi.fn<() => Promise>().mockResolvedValue({}), refetch: vi.fn(), utxoFrozen: false, extraUtxos: [] as Array<{ utxo: string; value: number; frozen: boolean }>, @@ -24,13 +26,16 @@ vi.mock('react-i18next', () => ({ })) vi.mock('@tanstack/react-query', () => ({ - useQuery: () => h.queryReturn, - useMutation: () => ({ mutateAsync: h.mutateAsync, isPending: h.isPending }), + useQuery: () => h.fetchAddressQueryData, + useMutation: vi.fn((options: MutationOptions) => ({ + isPending: false, + mutateAsync: async (input: unknown) => await options.mutationFn(input), + })), })) -vi.mock('@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query', () => ({ - directsendMutation: vi.fn(() => ({})), - freezeMutation: vi.fn(() => ({})), +vi.mock('@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query', () => ({ + directsendMutation: vi.fn(() => ({ mutationFn: h.directSendAsync })), + freezeMutation: vi.fn(() => ({ mutationFn: h.freezeAsync })), gettimelockaddressOptions: vi.fn(() => ({ queryKey: ['mock'], queryFn: vi.fn() })), })) @@ -56,7 +61,7 @@ vi.mock('@/context/JamWalletInfoContext', () => ({ vi.mock('@/lib/errorReason', () => ({ getErrorReason: () => 'reason' })) vi.mock('@/lib/fidelityBondUtils', () => ({ - lockdate: { toTimestamp: () => 1_893_456_000_000 }, + lockdate: { toDateLabel: () => 'January 1, 2030' }, })) vi.mock('@/lib/utils', async (importOriginal) => ({ @@ -136,6 +141,10 @@ vi.mock('@/components/ui/jam/CopyButton', () => ({ CopyButton: ({ value }: ValueProps) =>
{value}
, })) +vi.mock('@/components/ui/jam/Balance', () => ({ + Balance: ({ valueString }: { valueString: string }) => {valueString} sats, +})) + vi.mock('@/components/ui/label', () => ({ Label: ({ children }: ChildrenProps) => })) vi.mock('../ui/jam/Address', () => ({ @@ -166,15 +175,15 @@ const selectYear = (value: string) => { const goToConfirm = () => { selectMonth('06') - fireEvent.click(screen.getByText('earn.fidelity_bond.select_date.text_primary_button')) + fireEvent.click(screen.getByText('global.next')) } describe('RenewBondDialog', () => { beforeEach(() => { vi.clearAllMocks() - h.queryReturn = { data: { address: 'bc1timelock' }, isLoading: false, isError: false } - h.mutateAsync = vi.fn<() => Promise>().mockResolvedValue({ txinfo: { txid: 'abcd1234' } }) - h.isPending = false + h.fetchAddressQueryData = { data: { address: 'bc1timelock' }, isLoading: false, isError: false } + h.directSendAsync = vi.fn<() => Promise>().mockResolvedValue({ txinfo: { txid: 'abcd1234' } }) + h.freezeAsync = vi.fn<() => Promise>().mockResolvedValue({}) h.utxoFrozen = false h.extraUtxos = [] h.selectHandlers = [] @@ -189,13 +198,13 @@ describe('RenewBondDialog', () => { it('selecting a month enables the next button and shows the chosen date', () => { renderDialog() selectMonth('06') - expect(screen.getByText('earn.fidelity_bond.review_inputs.label_lock_date')).toBeInTheDocument() + expect(screen.getByText('earn.fidelity_bond.select_date.label_selected_lock_date')).toBeInTheDocument() }) it('selecting a year computes a clamped lock date', () => { renderDialog() selectYear('2027') - expect(screen.getByText('earn.fidelity_bond.review_inputs.label_lock_date')).toBeInTheDocument() + expect(screen.getByText('earn.fidelity_bond.select_date.label_selected_lock_date')).toBeInTheDocument() }) it('selecting an early month bumps the year forward', () => { @@ -203,7 +212,7 @@ describe('RenewBondDialog', () => { // a month earlier than the minimum month forces year = minYear + 1 selectMonth('06') selectYear('2026') - expect(screen.getByText('earn.fidelity_bond.review_inputs.label_lock_date')).toBeInTheDocument() + expect(screen.getByText('earn.fidelity_bond.select_date.label_selected_lock_date')).toBeInTheDocument() }) it('moves to the confirm step and shows the timelock address', () => { @@ -213,27 +222,33 @@ describe('RenewBondDialog', () => { }) it('shows a loading spinner while the timelock address loads', () => { - h.queryReturn = { data: undefined, isLoading: true, isError: false } + h.fetchAddressQueryData = { data: undefined, isLoading: true, isError: false } renderDialog() goToConfirm() expect(screen.getByText('earn.fidelity_bond.renew.text_loading')).toBeInTheDocument() }) it('shows an error alert when the address query fails', () => { - h.queryReturn = { data: undefined, isLoading: false, isError: true } + h.fetchAddressQueryData = { data: undefined, isLoading: false, isError: true } renderDialog() expect(screen.getByText('earn.fidelity_bond.error_loading_address')).toBeInTheDocument() }) it('completes the renew flow and reaches the success step', async () => { h.extraUtxos = [{ utxo: 'tx2:0', value: 500, frozen: false }] + renderDialog() + + expect(screen.queryByText('earn.fidelity_bond.renew.success_text')).not.toBeInTheDocument() + goToConfirm() + fireEvent.click(screen.getByTestId('confirm-switch')) fireEvent.click(screen.getByText('earn.fidelity_bond.renew.text_button_submit')) await waitFor(() => expect(screen.getByText('earn.fidelity_bond.renew.success_text')).toBeInTheDocument()) expect(screen.getAllByText('abcd1234').length).toBeGreaterThan(0) + expect(h.refetch).toHaveBeenCalled() }) @@ -245,13 +260,40 @@ describe('RenewBondDialog', () => { fireEvent.click(screen.getByText('earn.fidelity_bond.renew.text_button_submit')) await waitFor(() => expect(screen.getByText('earn.fidelity_bond.renew.success_text')).toBeInTheDocument()) - const calls = h.mutateAsync.mock.calls as unknown as Array<[{ body?: { freeze?: boolean } }]> - expect(calls.some((call) => call[0]?.body?.freeze === false)).toBe(true) + const calls = h.freezeAsync.mock.calls as unknown as Array< + [{ body?: { 'utxo-string'?: string; freeze?: boolean } }] + > + expect( + calls.some((call) => call[0]?.body?.['utxo-string'] === frozenUtxo.utxo && call[0]?.body?.freeze === false), + ).toBe(true) + }) + + it('freezes a fidelity bond after sending', async () => { + const frozenUtxo = { ...utxo, frozen: true } as unknown as FidelityBondUtxo + render() + goToConfirm() + fireEvent.click(screen.getByTestId('confirm-switch')) + fireEvent.click(screen.getByText('earn.fidelity_bond.renew.text_button_submit')) + + await waitFor(() => expect(screen.getByText('earn.fidelity_bond.renew.success_text')).toBeInTheDocument()) + + const calls = h.freezeAsync.mock.calls as unknown as Array< + [{ body?: { 'utxo-string'?: string; freeze?: boolean } }] + > + // unfreezes the expired fb utxo + expect( + calls.some((call) => call[0]?.body?.['utxo-string'] === frozenUtxo.utxo && call[0]?.body?.freeze === false), + ).toBe(true) + + // freezes the renewed fb utxo + expect( + calls.some((call) => call[0]?.body?.['utxo-string'] === 'abcd1234:0' && call[0]?.body?.freeze === true), + ).toBe(true) }) it('returns to the confirm step when sending fails', async () => { h.extraUtxos = [{ utxo: 'tx2:0', value: 500, frozen: false }] - h.mutateAsync = vi.fn<() => Promise>().mockRejectedValue(new Error('send failed')) + h.directSendAsync = vi.fn<() => Promise>().mockRejectedValue(new Error('send failed')) renderDialog() goToConfirm() fireEvent.click(screen.getByTestId('confirm-switch')) diff --git a/src/components/earn/RenewBondDialog.tsx b/src/components/earn/RenewBondDialog.tsx index de458991..6896c20d 100644 --- a/src/components/earn/RenewBondDialog.tsx +++ b/src/components/earn/RenewBondDialog.tsx @@ -1,50 +1,41 @@ -import { useState, useMemo } from 'react' -import { - directsendMutation, - freezeMutation, - gettimelockaddressOptions, -} from '@joinmarket-webui/joinmarket-api-ts/@tanstack/react-query' -import type { DirectSendResponse, ErrorMessage } from '@joinmarket-webui/joinmarket-api-ts/jm' -import { useMutation, useQuery } from '@tanstack/react-query' -import { - AlertTriangleIcon, - CalendarIcon, - CheckCircle2Icon, - CheckIcon, - ChevronLeftIcon, - CopyIcon, - RefreshCwIcon, -} from 'lucide-react' +import { useState } from 'react' +import { yupResolver } from '@hookform/resolvers/yup' +import { gettimelockaddressOptions } from '@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query' +import type { DirectSendResponse } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' +import { useQuery } from '@tanstack/react-query' +import { AlertTriangleIcon, CalendarIcon, RefreshCwIcon } from 'lucide-react' +import { useForm, useWatch } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Button } from '@/components/ui/button' -import { buttonVariants } from '@/components/ui/button-variants' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog' -import { CopyButton } from '@/components/ui/jam/CopyButton' -import { Label } from '@/components/ui/label' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { Spinner } from '@/components/ui/spinner' -import { Switch } from '@/components/ui/switch' -import { useJamWalletInfoContext } from '@/context/JamWalletInfoContext' +import { JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS } from '@/constants/jam' import { useApiClient } from '@/hooks/useApiClient' -import type { FidelityBondUtxo, Utxo } from '@/hooks/useQueryUtxos' -import { getErrorReason } from '@/lib/errorReason' +import type { FidelityBondUtxo } from '@/hooks/useQueryUtxos' import * as fb from '@/lib/fidelityBondUtils' -import { cn, formatSats, type WalletFileName } from '@/lib/utils' -import { useDeveloperMode } from '@/store/jamSettingsStore' -import { Address } from '../ui/jam/Address' -import { generateLockdateOptions, getYearOptions, getMonthOptions } from './CreateFidelityBondDialog/types' +import type { WalletFileName } from '@/lib/utils' +import { RENEW_BOND_FORM_DEFAULT_VALUES, renewBondFormSchema, type RenewBondFormValues } from './RenewBondDialog.schema' +import { FidelityBondDialogLayout } from './fidelity-bond/FidelityBondDialogLayout' +import { + AddressPreview, + ConfirmationToggle, + CopyableField, + FidelityBondAmount, + InfoCard, + InlineLoading, + JarBadge, + PendingStep, + StepIntro, + SuccessHeading, +} from './fidelity-bond/FidelityBondDialogParts' +import { LockdateSelect } from './fidelity-bond/LockdateSelect' +import { WizardStepFooter } from './fidelity-bond/WizardStepFooter' +import { useFidelityBondSweep } from './fidelity-bond/useFidelityBondSweep' type Step = 'select_date' | 'confirm' | 'sending' | 'success' +const WIZARD_STEPS: Step[] = ['select_date', 'confirm'] + interface RenewBondDialogProps { open: boolean onOpenChange: (open: boolean) => void @@ -55,46 +46,35 @@ interface RenewBondDialogProps { export function RenewBondDialog({ open, onOpenChange, walletFileName, utxo }: RenewBondDialogProps) { const { t } = useTranslation() const client = useApiClient() - const walletInfo = useJamWalletInfoContext() - const { enabled: isDeveloperMode } = useDeveloperMode() const [step, setStep] = useState('select_date') - const [selectedLockdate, setSelectedLockdate] = useState('') - const [confirmationChecked, setConfirmationChecked] = useState(false) const [txResult, setTxResult] = useState() - const [error, setError] = useState() - const lockdateOptions = useMemo(() => generateLockdateOptions(isDeveloperMode), [isDeveloperMode]) - const yearOptions = useMemo(() => getYearOptions(lockdateOptions), [lockdateOptions]) - const monthOptions = useMemo(() => getMonthOptions(), []) + const { + control, + handleSubmit, + reset, + setValue, + formState: { isSubmitting }, + } = useForm({ + mode: 'onChange', + defaultValues: RENEW_BOND_FORM_DEFAULT_VALUES, + resolver: yupResolver(renewBondFormSchema), + }) + const selectedLockdate = useWatch({ control, name: 'lockdate' }) + const confirmationChecked = useWatch({ + control, + name: 'confirmationAccepted', + }) - const minLockdate = lockdateOptions.at(0)?.value ?? '' - const maxLockdate = lockdateOptions.at(-1)?.value ?? '' - const clampLockdate = (lockdate: string): fb.Lockdate | '' => { - if (!lockdate || lockdate < minLockdate) return minLockdate || '' - if (lockdate > maxLockdate) return maxLockdate || '' - return lockdate as fb.Lockdate - } - const selectedYear = selectedLockdate ? selectedLockdate.slice(0, 4) : '' - const selectedMonth = selectedLockdate ? selectedLockdate.slice(5, 7) : '' - const minYear = minLockdate ? Number.parseInt(minLockdate.slice(0, 4), 10) : 0 - const minMonth = minLockdate ? Number.parseInt(minLockdate.slice(5, 7), 10) : 1 + const { sweep, isLoading, error, setError, sourceJar } = useFidelityBondSweep({ + walletFileName, + utxo, + unfreezeErrorKey: 'earn.fidelity_bond.error_unfreezing_utxos', + sendErrorKey: 'earn.fidelity_bond.renew.error_renewing_fidelity_bond', + }) - const selectedDateLabel = selectedLockdate - ? new Date(fb.lockdate.toTimestamp(selectedLockdate)).toLocaleDateString(undefined, { - year: 'numeric', - month: 'long', - day: 'numeric', - }) - : null - - const sourceJar = walletInfo.jars.find((jar) => jar.jarIndex === utxo.mixdepth) - - // UTXOs in the source jar that are NOT this FB — they need to be frozen during sweep - const utxosToFreeze = useMemo(() => { - if (!sourceJar) return [] - return sourceJar.utxos.filter((u) => u.utxo !== utxo.utxo && !u.frozen) - }, [sourceJar, utxo.utxo]) + const selectedDateLabel = selectedLockdate ? fb.lockdate.toDateLabel(selectedLockdate) : null const timelockAddressQuery = useQuery({ ...gettimelockaddressOptions({ @@ -109,40 +89,13 @@ export function RenewBondDialog({ open, onOpenChange, walletFileName, utxo }: Re retry: false, }) - if (timelockAddressQuery.isError && !error) { - setError(t('earn.fidelity_bond.error_loading_address')) - } - const destinationAddress = timelockAddressQuery.data?.address - - const freezeUtxo = useMutation({ - ...freezeMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.error_freezing_utxos')} ${reason}`) - }, - }) - - const unfreezeUtxo = useMutation({ - ...freezeMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.error_unfreezing_utxos')} ${reason}`) - }, - }) - - const directSend = useMutation({ - ...directsendMutation({ client }), - onError: (error: ErrorMessage) => { - const reason = getErrorReason(error, t('global.errors.reason_unknown')) - setError(`${t('earn.fidelity_bond.renew.error_renewing_fidelity_bond')} ${reason}`) - }, - }) + const displayError = + error ?? (timelockAddressQuery.isError ? t('earn.fidelity_bond.error_loading_address') : undefined) const handleReset = () => { setStep('select_date') - setSelectedLockdate('') - setConfirmationChecked(false) + reset(RENEW_BOND_FORM_DEFAULT_VALUES) setTxResult(undefined) setError(undefined) } @@ -154,348 +107,161 @@ export function RenewBondDialog({ open, onOpenChange, walletFileName, utxo }: Re onOpenChange(newOpen) } - const handleSubmit = async () => { + const submitRenewal = handleSubmit(async () => { if (!destinationAddress) return setStep('sending') - setError(undefined) - const frozen: Utxo[] = [] - try { - // Freeze other UTXOs in the source jar so only the FB gets swept - for (const u of utxosToFreeze) { - await freezeUtxo.mutateAsync({ - path: { walletname: walletFileName }, - body: { 'utxo-string': u.utxo, freeze: true }, - }) - frozen.push(u) - } + const txResult = await sweep({ + destination: destinationAddress, + tryFreezeAfterBroadcast: JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS, + }) - if (utxo.frozen) { - await unfreezeUtxo.mutateAsync({ - path: { walletname: walletFileName }, - body: { 'utxo-string': utxo.utxo, freeze: false }, - }) - } - - const result = await directSend.mutateAsync({ - path: { walletname: walletFileName }, - body: { - mixdepth: utxo.mixdepth, - amount_sats: 0, - destination: destinationAddress, - }, - }) - - setTxResult(result) + setTxResult(txResult) + if (txResult) { setStep('success') toast.success(t('earn.fidelity_bond.renew.success_text')) - - // Best-effort cleanup — tx already broadcast, don't throw on unfreeze failure - for (const u of frozen) { - try { - await unfreezeUtxo.mutateAsync({ - path: { walletname: walletFileName }, - body: { 'utxo-string': u.utxo, freeze: false }, - }) - } catch { - // logged via onError - } - } - - await walletInfo.refetch() - } catch { - // Best-effort rollback — unfreeze UTXOs that were frozen before the error - for (const u of frozen) { - try { - await unfreezeUtxo.mutateAsync({ - path: { walletname: walletFileName }, - body: { 'utxo-string': u.utxo, freeze: false }, - }) - } catch { - // logged via onError - } - } + } else { setStep('confirm') } + }) + + const renderFooter = () => { + switch (step) { + case 'select_date': + return ( + handleOpenChange(false)} + onPrimary={() => setStep('confirm')} + primaryDisabled={!selectedLockdate} + primaryLabel={t('global.next')} + /> + ) + case 'confirm': + return ( + setStep('select_date')} + onCancel={() => handleOpenChange(false)} + onPrimary={() => void submitRenewal()} + primaryDisabled={!confirmationChecked || !destinationAddress} + isLoading={isLoading || isSubmitting} + primaryLabel={t('earn.fidelity_bond.renew.text_button_submit')} + /> + ) + case 'sending': + return null + case 'success': + return + } } - const isLoading = freezeUtxo.isPending || unfreezeUtxo.isPending || directSend.isPending - return ( - - - - {t('earn.fidelity_bond.renew.title')} - {t('earn.fidelity_bond.subtitle')} - + + {step === 'select_date' && ( +
+ - {error && ( - - - {error} + + + + + + setValue('lockdate', lockdate || undefined, { + shouldDirty: true, + shouldTouch: true, + shouldValidate: true, + }) + } + /> +
+ )} + + {step === 'confirm' && ( +
+
+ +

{selectedDateLabel}

+
+ + + +
+ + + + + + {timelockAddressQuery.isLoading ? ( + + ) : ( + destinationAddress && ( + + ) + )} + + + + {t('earn.fidelity_bond.renew.confirm_send_modal.title')} + + {t('earn.fidelity_bond.confirm_modal.body', { + /* TODO: fix human readable duration */ + humanReadableDuration: selectedDateLabel ? `until ${selectedDateLabel}` : '', + date: selectedDateLabel || '', + })} + - )} -
- {step === 'select_date' && ( -
-
-
- -
-
-

{t('earn.fidelity_bond.select_date.description')}

-
-
+ + setValue('confirmationAccepted', checked, { + shouldDirty: true, + shouldTouch: true, + shouldValidate: true, + }) + } + /> +
+ )} -
-

- {t('earn.fidelity_bond.review_inputs.label_amount')} -

-

{formatSats(utxo.value)}

-
+ {step === 'sending' && } -
-
- - -
-
- - -
-
+ {step === 'success' && ( +
+ - {selectedLockdate && ( -
-

- {t('earn.fidelity_bond.review_inputs.label_lock_date')} -

-

{selectedDateLabel}

-
- )} -
+ {destinationAddress && ( + )} - {step === 'confirm' && ( -
-
-
-

- {t('earn.fidelity_bond.review_inputs.label_lock_date')} -

-

{selectedDateLabel}

-
-
-

{t('earn.fidelity_bond.review_inputs.label_jar')}

-

- {t('earn.fidelity_bond.review_inputs.label_jar_n', { jar: utxo.mixdepth })} -

-
-
- -
-

{t('earn.fidelity_bond.review_inputs.label_amount')}

-

{formatSats(utxo.value)}

-
- - {timelockAddressQuery.isLoading ? ( -
- - {t('earn.fidelity_bond.renew.text_loading')} -
- ) : ( - destinationAddress && ( -
- -
-
-
-
- ) - )} - - - - {t('earn.fidelity_bond.renew.confirm_send_modal.title')} - - {t('earn.fidelity_bond.confirm_modal.body', { - /* TODO: fix human readable duration */ - humanReadableDuration: selectedDateLabel ? `until ${selectedDateLabel}` : '', - date: selectedDateLabel || '', - })} - - - -
- setConfirmationChecked(checked)} - /> -
- -

- {t('earn.fidelity_bond.create_form.confirmation_toggle_subtitle')} -

-
-
-
- )} - - {step === 'sending' && ( -
-
- -
- -
-
-

{t('earn.fidelity_bond.renew.text_sending')}

-
- )} - - {step === 'success' && ( -
-
-
- -
-

{t('earn.fidelity_bond.renew.success_text')}

-
- - {destinationAddress && ( -
- -
- - {destinationAddress} - - } - successText={} - className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'h-10 w-10 shrink-0')} - onSuccess={() => toast.success(t('receive.text_copy_address'))} - onError={() => toast.error(t('global.errors.reason_unknown'))} - /> -
-
- )} - - {txResult?.txinfo?.txid && ( -
- -
- - {txResult.txinfo.txid} - - } - successText={} - className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'h-10 w-10 shrink-0')} - onSuccess={() => - toast.success(t('earn.fidelity_bond.create_fidelity_bond.text_copy_transaction_id')) - } - onError={() => toast.error(t('global.errors.reason_unknown'))} - /> -
-
- )} -
+ {txResult?.txinfo?.txid && ( + )}
- - {step === 'select_date' && ( - - - - - )} - - {step === 'confirm' && ( - - - - - - )} - - {step === 'success' && ( - - - - )} - -
+ )} + ) } diff --git a/src/components/earn/fidelity-bond/FidelityBondDialogLayout.tsx b/src/components/earn/fidelity-bond/FidelityBondDialogLayout.tsx new file mode 100644 index 00000000..443522a5 --- /dev/null +++ b/src/components/earn/fidelity-bond/FidelityBondDialogLayout.tsx @@ -0,0 +1,59 @@ +import type { ComponentProps, ReactNode } from 'react' +import { AlertTriangleIcon } from 'lucide-react' +import { useTranslation } from 'react-i18next' +import { Alert, AlertDescription } from '@/components/ui/alert' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { StepProgress } from './StepProgress' + +type FidelityBondDialogLayoutProps = Omit, 'children'> & { + title: string + /** zero-based wizard step, or undefined to hide the progress indicator (pending/success states) */ + currentStep?: number + totalSteps: number + error?: string + footer: ReactNode + children: ReactNode +} + +export function FidelityBondDialogLayout({ + title, + currentStep, + totalSteps, + error, + footer, + children, + ...dialogProps +}: FidelityBondDialogLayoutProps) { + const { t } = useTranslation() + return ( + + + + {title} + {t('earn.fidelity_bond.subtitle')} + + + {currentStep !== undefined && } + + {error && ( + + + {error} + + )} + + {/* negative margin + matching padding keep the scrollbar in the dialog padding, clear of content */} +
{children}
+ + {footer && {footer}} +
+
+ ) +} diff --git a/src/components/earn/fidelity-bond/FidelityBondDialogParts.tsx b/src/components/earn/fidelity-bond/FidelityBondDialogParts.tsx new file mode 100644 index 00000000..e50493ea --- /dev/null +++ b/src/components/earn/fidelity-bond/FidelityBondDialogParts.tsx @@ -0,0 +1,176 @@ +import type { ReactNode } from 'react' +import { CheckCircle2Icon, CheckIcon, CopyIcon, type LucideIcon } from 'lucide-react' +import { useTranslation } from 'react-i18next' +import { toast } from 'sonner' +import { Badge } from '@/components/ui/badge' +import { jarBadgeVariant } from '@/components/ui/badge-variants' +import { buttonVariants } from '@/components/ui/button-variants' +import { Item, ItemTitle, ItemDescription, ItemContent, ItemMedia } from '@/components/ui/item' +import { Address } from '@/components/ui/jam/Address' +import { Balance } from '@/components/ui/jam/Balance' +import { CopyButton } from '@/components/ui/jam/CopyButton' +import { Label } from '@/components/ui/label' +import { Spinner } from '@/components/ui/spinner' +import { Switch } from '@/components/ui/switch' +import { cn } from '@/lib/utils' +import type { AmountSats, JarIndex } from '@/types/global' + +/** icon + title/subtitle block shown at the top of a wizard step */ +export function StepIntro({ icon: Icon, title, subtitle }: { icon: LucideIcon; title: string; subtitle?: string }) { + return ( + + + + + + {title} + {subtitle} + + + ) +} + +/** labeled summary card used on review/confirm steps */ +export function InfoCard({ + label, + highlight = false, + children, +}: { + label: string + highlight?: boolean + children: ReactNode +}) { + return ( +
+

{label}

+ {children} +
+ ) +} + +export function FidelityBondAmount({ value, className }: { value: AmountSats; className?: string }) { + return +} + +/* TODO: move component to different namespace */ +export function JarBadge({ jarIndex, name }: { jarIndex: JarIndex | undefined; name?: string }) { + const { t } = useTranslation() + return ( + + {name ? ( + <> + {name} #{jarIndex} + + ) : ( + /* TODO: i18n - move key to different namespace */ + t('earn.fidelity_bond.review_inputs.label_jar_n', { jar: jarIndex }) + )} + + ) +} + +export function ConfirmationToggle({ + id, + checked, + onCheckedChange, +}: { + id: string + checked: boolean + onCheckedChange: (checked: boolean) => void +}) { + const { t } = useTranslation() + return ( +
+ +
+ +

+ {t('earn.fidelity_bond.create_form.confirmation_toggle_subtitle')} +

+
+
+ ) +} + +/** labeled value with a copy-to-clipboard button, used on success steps */ +export function CopyableField({ + label, + value, + copiedMessage, +}: { + label: string + value: string + copiedMessage: string +}) { + const { t } = useTranslation() + return ( +
+ +
+ {value} + } + successText={} + className={cn(buttonVariants({ variant: 'outline', size: 'icon' }), 'h-10 w-10 shrink-0')} + onSuccess={() => toast.success(copiedMessage)} + onError={(error) => { + const reason = (error instanceof Error ? error.message : undefined) || t('global.errors.reason_unknown') + toast.error(t('global.errors.error_copy_to_clipboard_failed', { reason })) + }} + /> +
+
+ ) +} + +/** labeled read-only address preview, used on confirm steps */ +export function AddressPreview({ label, address }: { label: string; address: string }) { + return ( +
+ +
+
+
+
+ ) +} + +export function InlineLoading({ text }: { text: string }) { + return ( +
+ + {text} +
+ ) +} + +/** full-step spinner shown while a transaction is in flight */ +export function PendingStep({ icon: Icon, title, subtitle }: { icon: LucideIcon; title: string; subtitle?: string }) { + return ( +
+
+ +
+ +
+
+

{title}

+ {subtitle &&

{subtitle}

} +
+ ) +} + +export function SuccessHeading({ title, subtitle }: { title: string; subtitle?: string }) { + return ( +
+
+ +
+

{title}

+ {subtitle &&

{subtitle}

} +
+ ) +} diff --git a/src/components/earn/fidelity-bond/FidelityBondJarSelector.tsx b/src/components/earn/fidelity-bond/FidelityBondJarSelector.tsx new file mode 100644 index 00000000..db4db5f7 --- /dev/null +++ b/src/components/earn/fidelity-bond/FidelityBondJarSelector.tsx @@ -0,0 +1,33 @@ +import { SelectableJar } from '@/components/ui/jam/SelectableJar' +import { useJamWalletInfoContext, type Jar } from '@/context/JamWalletInfoContext' +import type { JarIndex } from '@/types/global' + +type FidelityBondJarSelectorProps = { + selectedJarIndex: JarIndex | undefined + onSelect: (jarIndex: JarIndex) => void + isJarDisabled: (jar: Jar) => boolean +} + +/** jar selection grid shared by the fidelity bond flows, same look as the send flow's jar selector */ +export function FidelityBondJarSelector({ selectedJarIndex, onSelect, isJarDisabled }: FidelityBondJarSelectorProps) { + const { jars, walletBalanceSummary } = useJamWalletInfoContext() + + return ( +
+ {jars.map((jar) => ( + onSelect(jar.jarIndex)} + disabled={isJarDisabled(jar)} + /> + ))} +
+ ) +} diff --git a/src/components/earn/fidelity-bond/LockdateSelect.tsx b/src/components/earn/fidelity-bond/LockdateSelect.tsx new file mode 100644 index 00000000..4dd6f133 --- /dev/null +++ b/src/components/earn/fidelity-bond/LockdateSelect.tsx @@ -0,0 +1,114 @@ +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { Label } from '@/components/ui/label' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import * as fb from '@/lib/fidelityBondUtils' +import { useDeveloperMode } from '@/store/jamSettingsStore' +import { generateLockdateOptions, getMonthOptions, getYearOptions } from '../CreateFidelityBondDialog/types' + +type LockdateSelectProps = { + /** prefix for the month/year element ids */ + id: string + value: fb.Lockdate | '' + onChange: (value: fb.Lockdate | '') => void + unavailableValues?: readonly fb.Lockdate[] +} + +export function LockdateSelect({ id, value, onChange, unavailableValues = [] }: LockdateSelectProps) { + const { t } = useTranslation() + const { enabled: isDeveloperMode } = useDeveloperMode() + + const lockdateOptions = useMemo(() => generateLockdateOptions(isDeveloperMode), [isDeveloperMode]) + const unavailableSet = useMemo(() => new Set(unavailableValues), [unavailableValues]) + const availableLockdateOptions = useMemo( + () => lockdateOptions.filter((option) => !unavailableSet.has(option.value)), + [lockdateOptions, unavailableSet], + ) + const yearOptions = useMemo(() => getYearOptions(lockdateOptions), [lockdateOptions]) + const monthOptions = useMemo(() => getMonthOptions(), []) + + const selectedYear = value ? value.slice(0, 4) : '' + const selectedMonth = value ? value.slice(5, 7) : '' + + return ( + <> +
+
+ + +
+
+ + +
+
+ + {value && ( +
+

+ {t('earn.fidelity_bond.select_date.label_selected_lock_date')} +

+

{fb.lockdate.toDateLabel(value)}

+
+ )} + + ) +} diff --git a/src/components/earn/CreateFidelityBondDialog/StepProgress.test.tsx b/src/components/earn/fidelity-bond/StepProgress.test.tsx similarity index 60% rename from src/components/earn/CreateFidelityBondDialog/StepProgress.test.tsx rename to src/components/earn/fidelity-bond/StepProgress.test.tsx index 825a33e5..5a9c2dbb 100644 --- a/src/components/earn/CreateFidelityBondDialog/StepProgress.test.tsx +++ b/src/components/earn/fidelity-bond/StepProgress.test.tsx @@ -1,22 +1,29 @@ -import { render } from '@testing-library/react' +import { render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' import { StepProgress } from './StepProgress' +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string, options?: Record) => key + (options ? ' ' + JSON.stringify(options) : ''), + }), +})) + vi.mock('@/lib/utils', () => ({ cn: (...args: unknown[]) => args.filter(Boolean).join(' '), })) describe('StepProgress', () => { - it('renders correct number of steps', () => { + it('renders correct number of steps and the step label', () => { const { container } = render() - const steps = container.firstChild?.childNodes + const steps = container.querySelectorAll('.rounded-full') expect(steps).toHaveLength(3) + expect(screen.getByText('global.step_label {"current":1,"total":3}')).toBeInTheDocument() }) it('applies correct classes based on currentStep', () => { const { container } = render() - const steps = container.firstChild?.childNodes as NodeListOf + const steps = container.querySelectorAll('.rounded-full') // Step 0: completed (i < currentStep) expect(steps[0].className).toContain('bg-primary w-8') diff --git a/src/components/earn/fidelity-bond/StepProgress.tsx b/src/components/earn/fidelity-bond/StepProgress.tsx new file mode 100644 index 00000000..25125b6f --- /dev/null +++ b/src/components/earn/fidelity-bond/StepProgress.tsx @@ -0,0 +1,29 @@ +import { useTranslation } from 'react-i18next' +import { cn } from '@/lib/utils' + +type StepProgressProps = { + currentStep: number + totalSteps: number +} + +export function StepProgress({ currentStep, totalSteps }: StepProgressProps) { + const { t } = useTranslation() + return ( +
+
+ {Array.from({ length: totalSteps }, (_, i) => ( +
+ ))} +
+

+ {t('global.step_label', { current: currentStep + 1, total: totalSteps })} +

+
+ ) +} diff --git a/src/components/earn/fidelity-bond/WizardStepFooter.tsx b/src/components/earn/fidelity-bond/WizardStepFooter.tsx new file mode 100644 index 00000000..f8643ff4 --- /dev/null +++ b/src/components/earn/fidelity-bond/WizardStepFooter.tsx @@ -0,0 +1,43 @@ +import type { ReactNode } from 'react' +import { ChevronLeftIcon } from 'lucide-react' +import { useTranslation } from 'react-i18next' +import { Button } from '@/components/ui/button' +import { Spinner } from '@/components/ui/spinner' + +type WizardStepFooterProps = { + /** omit to hide the back button (first step) */ + onBack?: () => void + onCancel: () => void + primaryLabel: ReactNode + onPrimary: () => void + primaryDisabled?: boolean + isLoading?: boolean +} + +export function WizardStepFooter({ + onBack, + onCancel, + primaryLabel, + onPrimary, + primaryDisabled = false, + isLoading = false, +}: WizardStepFooterProps) { + const { t } = useTranslation() + return ( + <> + {onBack && ( + + )} + + + + ) +} diff --git a/src/components/earn/fidelity-bond/useFidelityBondMutations.ts b/src/components/earn/fidelity-bond/useFidelityBondMutations.ts new file mode 100644 index 00000000..c47f4d5a --- /dev/null +++ b/src/components/earn/fidelity-bond/useFidelityBondMutations.ts @@ -0,0 +1,46 @@ +import { useState } from 'react' +import { directsendMutation, freezeMutation } from '@joinmarket-webui/joinmarket-ng-api-ts/@tanstack/react-query' +import { useMutation } from '@tanstack/react-query' +import { useTranslation } from 'react-i18next' +import { useApiClient } from '@/hooks/useApiClient' +import { getErrorReason } from '@/lib/errorReason' + +/** + * The freeze/unfreeze/direct-send mutations shared by all fidelity bond flows, + * reporting failures through a common error state. The unfreeze/send error + * messages differ per flow (create/renew/move) and are passed as i18n keys. + */ +export function useFidelityBondMutations({ + unfreezeErrorKey, + sendErrorKey, +}: { + unfreezeErrorKey: string + sendErrorKey: string +}) { + const { t } = useTranslation() + const client = useApiClient() + const [error, setError] = useState() + + const setErrorWithReason = (error: unknown, message: string) => + setError(`${message} ${getErrorReason(error, t('global.errors.reason_unknown'))}`) + + const freezeUtxo = useMutation({ + ...freezeMutation({ client }), + retry: false, + onError: (error) => setErrorWithReason(error, t('global.errors.error_freezing_utxos')), + }) + + const unfreezeUtxo = useMutation({ + ...freezeMutation({ client }), + retry: false, + onError: (error) => setErrorWithReason(error, t(unfreezeErrorKey)), + }) + + const directSend = useMutation({ + ...directsendMutation({ client }), + retry: false, + onError: (error) => setErrorWithReason(error, t(sendErrorKey)), + }) + + return { freezeUtxo, unfreezeUtxo, directSend, error, setError } +} diff --git a/src/components/earn/fidelity-bond/useFidelityBondSweep.test.ts b/src/components/earn/fidelity-bond/useFidelityBondSweep.test.ts new file mode 100644 index 00000000..ca985da0 --- /dev/null +++ b/src/components/earn/fidelity-bond/useFidelityBondSweep.test.ts @@ -0,0 +1,208 @@ +import { renderHook } from '@testing-library/react' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import type { Jar } from '@/context/JamWalletInfoContext' +import type { FidelityBondUtxo, Utxo } from '@/hooks/useQueryUtxos' +import { BALANCE_SUMMARY_EMPTY } from '@/lib/balanceSummary' +import { useFidelityBondSweep } from './useFidelityBondSweep' + +const mocks = vi.hoisted(() => ({ + freezeMutateAsync: vi.fn(), + unfreezeMutateAsync: vi.fn(), + directSendMutateAsync: vi.fn(), + setError: vi.fn(), + walletInfoRefetch: vi.fn(), + walletInfo: { + jars: [] as Jar[], + refetch: vi.fn(), + }, +})) + +// `useFidelityBondSweep` only talks to the network through these two hooks — +// mock at that boundary and let the hook's own orchestration logic run for real. +vi.mock('@tanstack/react-query', () => ({ + useMutation: vi.fn((options: { mutationFn: (variables: unknown) => Promise }) => ({ + mutateAsync: options.mutationFn, + isPending: false, + })), +})) + +vi.mock('./useFidelityBondMutations', () => ({ + useFidelityBondMutations: () => ({ + freezeUtxo: { mutateAsync: mocks.freezeMutateAsync }, + unfreezeUtxo: { mutateAsync: mocks.unfreezeMutateAsync }, + directSend: { mutateAsync: mocks.directSendMutateAsync }, + error: undefined, + setError: mocks.setError, + }), +})) + +vi.mock('@/context/JamWalletInfoContext', async (importOriginal) => ({ + ...(await importOriginal()), + useJamWalletInfoContext: () => mocks.walletInfo, +})) + +vi.mock('@/lib/utils', async (importOriginal) => ({ + ...(await importOriginal()), + delayedPromise: vi.fn(() => Promise.resolve()), +})) + +const utxo = (overrides: Partial): Utxo => ({ + utxo: 'tx:0', + address: 'bcrt1qsource', + path: "m/84'/1'/0'/0/0", + label: '', + value: 100_000, + tries: 0, + tries_remaining: 3, + external: false, + mixdepth: 0, + confirmations: 6, + frozen: false, + locktime: undefined, + ...overrides, +}) + +const bondUtxo = (overrides: Partial): FidelityBondUtxo => ({ + ...utxo({ utxo: 'bond:0', frozen: true, ...overrides }), + locktime: '2999-01-01 00:00:00', +}) + +const jar = (jarIndex: number, utxos: Utxo[]): Jar => + ({ + jarIndex, + name: `Jar ${jarIndex}`, + color: '#808080', + balanceSummary: BALANCE_SUMMARY_EMPTY, + utxos, + }) as unknown as Jar + +describe('useFidelityBondSweep', () => { + beforeEach(() => { + vi.clearAllMocks() + mocks.freezeMutateAsync.mockResolvedValue(undefined) + mocks.unfreezeMutateAsync.mockResolvedValue(undefined) + mocks.walletInfoRefetch.mockResolvedValue(undefined) + mocks.walletInfo.refetch = mocks.walletInfoRefetch + }) + + it('re-freezes the fidelity bond after a failed sweep that had unfrozen it', async () => { + const bond = bondUtxo({ mixdepth: 0 }) + const other = utxo({ utxo: 'other:0', mixdepth: 0, frozen: false }) + mocks.walletInfo.jars = [jar(0, [bond, other])] + mocks.directSendMutateAsync.mockRejectedValue(new Error('broadcast failed')) + + const { result } = renderHook(() => + useFidelityBondSweep({ + walletFileName: 'wallet.jmdat', + utxo: bond, + unfreezeErrorKey: 'earn.fidelity_bond.error_unfreezing_utxos', + sendErrorKey: 'earn.fidelity_bond.renew.error_renewing_fidelity_bond', + }), + ) + + const txResult = await result.current.sweep({ + destination: 'bcrt1qdestination', + tryFreezeAfterBroadcast: false, + }) + + expect(txResult).toBeUndefined() + + // the other jar utxo was frozen then correctly rolled back + expect(mocks.freezeMutateAsync).toHaveBeenCalledWith({ + path: { walletname: 'wallet.jmdat' }, + body: { 'utxo-string': 'other:0', freeze: true }, + }) + expect(mocks.unfreezeMutateAsync).toHaveBeenCalledWith( + expect.objectContaining({ body: { 'utxo-string': 'other:0', freeze: false } }), + ) + + // the bond itself was unfrozen before the failed send, and MUST be re-frozen in rollback + expect(mocks.unfreezeMutateAsync).toHaveBeenCalledWith({ + path: { walletname: 'wallet.jmdat' }, + body: { 'utxo-string': 'bond:0', freeze: false }, + }) + expect(mocks.freezeMutateAsync).toHaveBeenCalledWith( + expect.objectContaining({ body: { 'utxo-string': 'bond:0', freeze: true } }), + ) + }) + + it('does not attempt to re-freeze the bond on failure when it was never unfrozen', async () => { + const bond = bondUtxo({ mixdepth: 0, frozen: false }) + mocks.walletInfo.jars = [jar(0, [bond])] + mocks.directSendMutateAsync.mockRejectedValue(new Error('broadcast failed')) + + const { result } = renderHook(() => + useFidelityBondSweep({ + walletFileName: 'wallet.jmdat', + utxo: bond, + unfreezeErrorKey: 'earn.fidelity_bond.error_unfreezing_utxos', + sendErrorKey: 'earn.fidelity_bond.renew.error_renewing_fidelity_bond', + }), + ) + + await result.current.sweep({ destination: 'bcrt1qdestination', tryFreezeAfterBroadcast: false }) + + expect(mocks.unfreezeMutateAsync).not.toHaveBeenCalled() + expect(mocks.freezeMutateAsync).not.toHaveBeenCalledWith( + expect.objectContaining({ body: { 'utxo-string': 'bond:0', freeze: true } }), + ) + }) + + it('sweeps successfully: freezes other utxos, unfreezes the bond, then restores the others', async () => { + const bond = bondUtxo({ mixdepth: 0 }) + const other = utxo({ utxo: 'other:0', mixdepth: 0, frozen: false }) + mocks.walletInfo.jars = [jar(0, [bond, other])] + mocks.directSendMutateAsync.mockResolvedValue({ txinfo: { txid: 'renewed-tx' } }) + + const { result } = renderHook(() => + useFidelityBondSweep({ + walletFileName: 'wallet.jmdat', + utxo: bond, + unfreezeErrorKey: 'earn.fidelity_bond.error_unfreezing_utxos', + sendErrorKey: 'earn.fidelity_bond.renew.error_renewing_fidelity_bond', + }), + ) + + const txResult = await result.current.sweep({ + destination: 'bcrt1qdestination', + tryFreezeAfterBroadcast: false, + }) + + expect(txResult).toEqual({ txinfo: { txid: 'renewed-tx' } }) + expect(mocks.directSendMutateAsync).toHaveBeenCalledWith({ + path: { walletname: 'wallet.jmdat' }, + body: { mixdepth: 0, amount_sats: 0, destination: 'bcrt1qdestination' }, + }) + // the other utxo ends up unfrozen again post-broadcast + expect(mocks.unfreezeMutateAsync).toHaveBeenCalledWith( + expect.objectContaining({ body: { 'utxo-string': 'other:0', freeze: false } }), + ) + expect(mocks.walletInfoRefetch).toHaveBeenCalled() + }) + + it('does not re-freeze the bond if the sweep broadcast but the refetch after it fails', async () => { + const bond = bondUtxo({ mixdepth: 0 }) + mocks.walletInfo.jars = [jar(0, [bond])] + mocks.directSendMutateAsync.mockResolvedValue({ txinfo: { txid: 'renewed-tx' } }) + mocks.walletInfoRefetch.mockRejectedValue(new Error('refetch failed')) + + const { result } = renderHook(() => + useFidelityBondSweep({ + walletFileName: 'wallet.jmdat', + utxo: bond, + unfreezeErrorKey: 'earn.fidelity_bond.error_unfreezing_utxos', + sendErrorKey: 'earn.fidelity_bond.renew.error_renewing_fidelity_bond', + }), + ) + + const txResult = await result.current.sweep({ + destination: 'bcrt1qdestination', + tryFreezeAfterBroadcast: false, + }) + + expect(txResult).toEqual({ txinfo: { txid: 'renewed-tx' } }) + expect(mocks.freezeMutateAsync).not.toHaveBeenCalledWith( + expect.objectContaining({ body: { 'utxo-string': 'bond:0', freeze: true } }), + ) + }) +}) diff --git a/src/components/earn/fidelity-bond/useFidelityBondSweep.ts b/src/components/earn/fidelity-bond/useFidelityBondSweep.ts new file mode 100644 index 00000000..80c877e8 --- /dev/null +++ b/src/components/earn/fidelity-bond/useFidelityBondSweep.ts @@ -0,0 +1,172 @@ +import { useMemo } from 'react' +import type { DirectSendResponse } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' +import { useMutation } from '@tanstack/react-query' +import { JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS_DELAY } from '@/constants/jam' +import { useJamWalletInfoContext } from '@/context/JamWalletInfoContext' +import type { FidelityBondUtxo, Utxo } from '@/hooks/useQueryUtxos' +import { delayedPromise, type WalletFileName } from '@/lib/utils' +import { useFidelityBondMutations } from './useFidelityBondMutations' + +/** + * Sweeps a fidelity bond UTXO to a destination address: freezes all other + * UTXOs in the source jar, unfreezes the bond if needed, direct-sends the + * whole jar balance, then restores the frozen UTXOs. Shared by the renew + * and move-to-jar flows. + */ +export function useFidelityBondSweep({ + walletFileName, + utxo, + unfreezeErrorKey, + sendErrorKey, +}: { + walletFileName: WalletFileName + utxo: FidelityBondUtxo + unfreezeErrorKey: string + sendErrorKey: string +}) { + const { jars, refetch: walletInfoRefetch } = useJamWalletInfoContext() + const { freezeUtxo, unfreezeUtxo, directSend, error, setError } = useFidelityBondMutations({ + unfreezeErrorKey, + sendErrorKey, + }) + + const sourceJar = jars.find((jar) => jar.jarIndex === utxo.mixdepth) + + // UTXOs in the source jar that are NOT this FB — they need to be frozen during sweep + const utxosToFreeze = useMemo(() => { + if (!sourceJar) return [] + return sourceJar.utxos.filter((u) => u.utxo !== utxo.utxo && !u.frozen) + }, [sourceJar, utxo.utxo]) + + /** + * Runs the sweep. `onBroadcast` fires as soon as the transaction is sent, + * before the frozen UTXOs are restored. Returns false when the sweep failed + * (after a best-effort rollback). + */ + const { isPending, mutateAsync: sweep } = useMutation({ + mutationFn: async ({ + destination, + tryFreezeAfterBroadcast, + onBroadcastSuccess, + }: { + destination: string + tryFreezeAfterBroadcast: boolean + onBroadcastSuccess?: (result: DirectSendResponse) => Promise + }): Promise => { + setError(undefined) + + const frozen: Utxo[] = [] + let bondWasUnfrozen = false + let sweepBroadcasted = false + + try { + // Freeze other UTXOs in the source jar so only the FB gets swept + for (const u of utxosToFreeze) { + await freezeUtxo.mutateAsync({ + path: { walletname: walletFileName }, + body: { 'utxo-string': u.utxo, freeze: true }, + }) + frozen.push(u) + } + + if (utxo.frozen) { + await unfreezeUtxo.mutateAsync({ + path: { walletname: walletFileName }, + body: { 'utxo-string': utxo.utxo, freeze: false }, + }) + bondWasUnfrozen = true + } + + const result = await directSend.mutateAsync({ + path: { walletname: walletFileName }, + body: { + mixdepth: utxo.mixdepth, + amount_sats: 0, // 0 := sweep! + destination, + }, + }) + sweepBroadcasted = true + + // Best-effort cleanup — tx already broadcast, don't throw on unfreeze failure + for (const u of frozen) { + try { + await unfreezeUtxo.mutateAsync({ + path: { walletname: walletFileName }, + body: { 'utxo-string': u.utxo, freeze: false }, + throwOnError: true, + }) + } catch (_ignoredOnPurpose: unknown) { + // only debug output + console.debug('Error while unfreezing previously frozen UTXO.') + } + } + + if (tryFreezeAfterBroadcast) { + try { + await delayedPromise(JAM_TRY_FREEZE_CREATED_FIDELITY_BOND_OUTPUTS_DELAY) + + const fbUtxoId = `${result.txinfo.txid}:0` // sent with a sweep, so it is a single output + await freezeUtxo.mutateAsync({ + path: { walletname: walletFileName }, + body: { 'utxo-string': fbUtxoId, freeze: true }, + throwOnError: true, + }) + } catch (_ignoredOnPurpose: unknown) { + console.warn('Error while freezing Fidelity Bond UTXO - continuing.') + } + } + + try { + await onBroadcastSuccess?.(result) + } catch (error: unknown) { + console.warn('onBroadcastSuccess failed', error) + } + + return result + } catch (_ignoredOnPurpose: unknown) { + // Best-effort rollback — unfreeze UTXOs that were frozen before the error + for (const u of frozen) { + try { + await unfreezeUtxo.mutateAsync({ + path: { walletname: walletFileName }, + body: { 'utxo-string': u.utxo, freeze: false }, + throwOnError: true, + }) + } catch (_ignoredOnPurpose: unknown) { + // only debug output + console.debug('Error while unfreezing previously frozen UTXO in error handling.') + } + } + + // re-freeze the bond if it was unfrozen before the error. skip once the + // sweep already broadcast, the bond utxo is spent by then + if (bondWasUnfrozen && !sweepBroadcasted) { + try { + await freezeUtxo.mutateAsync({ + path: { walletname: walletFileName }, + body: { 'utxo-string': utxo.utxo, freeze: true }, + throwOnError: true, + }) + } catch (_ignoredOnPurpose: unknown) { + console.debug('Error while re-freezing fidelity bond UTXO in error handling.') + } + } + + return undefined + } finally { + // refetch only if the sweep broadcast, and don't let a refetch error + // get treated as a sweep failure + if (sweepBroadcasted) { + try { + await walletInfoRefetch() + } catch (error: unknown) { + console.warn('Error while refetching wallet info after sweep.', error) + } + } + } + }, + retry: false, + }) + + return { sweep, isLoading: isPending, error, setError, sourceJar } +} diff --git a/src/components/earn/report/EarnReportContent.test.tsx b/src/components/earn/report/EarnReportContent.test.tsx index 4ebb14e0..11955426 100644 --- a/src/components/earn/report/EarnReportContent.test.tsx +++ b/src/components/earn/report/EarnReportContent.test.tsx @@ -118,24 +118,49 @@ describe('EarnReportContent', () => { vi.restoreAllMocks() }) - it('renders loading and empty report states', () => { + it('renders loading and report states', () => { mocks.isLoading = true + mocks.isRefetching = false + mocks.entries = [] const { rerender } = render() expect(screen.getByText('spinner')).toBeInTheDocument() + expect(screen.queryByRole('button', { name: 'global.refresh' })).not.toBeInTheDocument() + expect(screen.queryByRole('button', { name: 'global.download' })).not.toBeInTheDocument() mocks.isLoading = false + mocks.isRefetching = true mocks.entries = [] rerender() expect(screen.getByText('earn.alert_empty_report')).toBeInTheDocument() - expect(screen.getByRole('button', { name: /earn\.report\.text_button_download_csv/u })).toBeDisabled() + expect(screen.getByRole('button', { name: 'global.refresh' })).toBeDisabled() + expect(screen.getByRole('button', { name: 'global.download' })).toBeDisabled() + + mocks.isLoading = false + mocks.isRefetching = false + mocks.entries = [] + rerender() + + expect(screen.queryByText('earn.alert_empty_report')).toBeInTheDocument() + expect(screen.getByRole('button', { name: 'global.refresh' })).toBeEnabled() + expect(screen.getByRole('button', { name: 'global.download' })).toBeDisabled() + + mocks.isLoading = false + mocks.isRefetching = false + mocks.entries = [entry({ earnedAmount: 100, notes: 'first maker note' })] + rerender() + + expect(screen.queryByText('earn.alert_empty_report')).not.toBeInTheDocument() + expect(screen.getByRole('button', { name: 'global.refresh' })).toBeEnabled() + expect(screen.getByRole('button', { name: 'global.download' })).toBeEnabled() }) it('summarizes, filters, refreshes, paginates, and exports report rows', async () => { const anchorClick = vi.fn() const createElement = vi.spyOn(document, 'createElement') createElement.mockImplementation((tagName: string) => { + // eslint-disable-next-line unicorn/prefer-https -- XHTML namespace URI is an identifier that must be http const element = document.createElementNS('http://www.w3.org/1999/xhtml', tagName) if (tagName === 'a') { Object.defineProperty(element, 'click', { @@ -151,37 +176,78 @@ describe('EarnReportContent', () => { expect(screen.getByText('chart:3')).toBeInTheDocument() expect(screen.getByText('balance:600')).toBeInTheDocument() expect(screen.getAllByText('balance:300').length).toBeGreaterThan(0) - expect(screen.getByText(/earn\.report\.text_report_summary:\{"count":3\}/u)).toBeInTheDocument() + expect(screen.getByText('earn.report.text_report_summary:{"count":3}')).toBeInTheDocument() + expect(screen.queryByText(/earn\.report\.text_report_summary_filtered:/u)).not.toBeInTheDocument() expect(screen.getByText('first maker note')).toBeInTheDocument() fireEvent.change(screen.getByPlaceholderText('earn.report.placeholder_search'), { target: { value: 'second' }, }) - expect(screen.getByText(/earn\.report\.text_report_summary_filtered/u)).toBeInTheDocument() + + expect(screen.queryByText(/earn\.report\.text_report_summary:/u)).not.toBeInTheDocument() + expect(screen.getByText('earn.report.text_report_summary_filtered:{"count":1}')).toBeInTheDocument() expect(screen.queryByText('first maker note')).not.toBeInTheDocument() expect(screen.getByText('second maker note')).toBeInTheDocument() - fireEvent.click(screen.getByRole('button', { name: /earn\.report\.text_button_download_csv/u })) + const downloadButton = screen.getByRole('button', { name: 'global.download' }) + expect(downloadButton).toBeEnabled() + + fireEvent.click(downloadButton) expect(mocks.createObjectURL).toHaveBeenCalled() expect(anchorClick).toHaveBeenCalled() fireEvent.click(screen.getByRole('button', { name: 'first-page' })) fireEvent.click(screen.getByRole('button', { name: 'show-all' })) fireEvent.click(screen.getByRole('button', { name: 'show-page-size' })) - fireEvent.click(screen.getByRole('button', { name: '' })) + + const refreshButton = screen.getByRole('button', { name: 'global.refresh' }) + expect(refreshButton).toBeEnabled() + + fireEvent.click(refreshButton) await waitFor(() => expect(mocks.refetch).toHaveBeenCalled()) createElement.mockRestore() }) + it('exports all filtered rows regardless of pagination', async () => { + mocks.entries = Array.from({ length: 30 }, (_, index) => + entry({ + notes: `maker note ${index + 1}`, + timestamp: new Date(Date.UTC(2026, 0, index + 1, 12)), + }), + ) + + render() + + expect(screen.getByText('pagination:30')).toBeInTheDocument() + expect(screen.getAllByText(/maker note/u)).toHaveLength(25) + + fireEvent.click(screen.getByRole('button', { name: 'global.download' })) + + const blob = mocks.createObjectURL.mock.calls[0]?.[0] + expect(blob).toBeInstanceOf(Blob) + if (!blob) { + throw new Error('CSV blob was not created') + } + + const csv = await blob.text() + const lines = csv.trim().split('\n') + + expect(lines).toHaveLength(31) + expect(lines[1]).toContain('2026-01-30T12:00:00.000Z') + expect(lines.at(-1)).toContain('2026-01-01T12:00:00.000Z') + }) + it('adds demo rows in developer mode', () => { mocks.developerMode = true render() + expect(screen.getByText('dev-badge')).toBeInTheDocument() + expect(screen.getByText('earn.report.text_report_summary:{"count":3}')).toBeInTheDocument() + fireEvent.click(screen.getByRole('button', { name: /earn\.report\.text_button_generate_demo_report/u })) - expect(screen.getByText('dev-badge')).toBeInTheDocument() - expect(screen.getByText(/earn\.report\.text_report_summary:\{"count":4\}/u)).toBeInTheDocument() + expect(screen.getByText('earn.report.text_report_summary:{"count":4}')).toBeInTheDocument() }) }) diff --git a/src/components/earn/report/EarnReportContent.tsx b/src/components/earn/report/EarnReportContent.tsx index 7902de48..6e6cf12b 100644 --- a/src/components/earn/report/EarnReportContent.tsx +++ b/src/components/earn/report/EarnReportContent.tsx @@ -11,14 +11,14 @@ import { type SortingState, useReactTable, } from '@tanstack/react-table' -import { DownloadIcon, PlusIcon, RefreshCwIcon, SearchIcon } from 'lucide-react' +import { DownloadIcon, PlusIcon, RefreshCwIcon, SearchIcon, XIcon } from 'lucide-react' import { useTranslation } from 'react-i18next' import { DevBadge } from '@/components/dev/DevBadge' import { useQueryYieldgenReport, type EarnReportEntry } from '@/components/earn/report/hooks/useQueryYieldgenReport' import { Alert, AlertTitle } from '@/components/ui/alert' import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' -import { Input } from '@/components/ui/input' +import { InputGroup, InputGroupAddon, InputGroupInput } from '@/components/ui/input-group' import { Balance } from '@/components/ui/jam/Balance' import { SortIcon } from '@/components/ui/jam/SortIcon' import { TablePagination } from '@/components/ui/jam/TablePagination' @@ -144,11 +144,11 @@ export const EarnReportContent = ({ className, enabled }: EarnReportContentProps }, [isShowAll, allEntries.length, table]) const visibleRows = table.getRowModel().rows + const exportRows = table.getSortedRowModel().rows - // Export visible entries as CSV const downloadCsv = useCallback(() => { const header = 'timestamp,cj_amount,input_count,input_amount,fee,earned,confirm_minutes,notes' - const rows = visibleRows.map((row) => + const rows = exportRows.map((row) => [ row.original.timestamp.toISOString(), row.original.cjTotalAmount ?? '', @@ -170,7 +170,7 @@ export const EarnReportContent = ({ className, enabled }: EarnReportContentProps setTimeout(() => { URL.revokeObjectURL(url) }, 0) - }, [visibleRows]) + }, [exportRows]) const earnedTotal = useMemo(() => sumEarned(allEntries, BITCOIN_GENESIS_DATE), [allEntries]) @@ -180,7 +180,7 @@ export const EarnReportContent = ({ className, enabled }: EarnReportContentProps const earned24Hours = useMemo(() => sumEarned(allEntries, new Date(now - MILLISECONDS_IN_A_DAY)), [allEntries, now]) return ( -
+
{isLoading ? (
@@ -188,7 +188,7 @@ export const EarnReportContent = ({ className, enabled }: EarnReportContentProps ) : (
{/* Stats row */} -
+
{( [ { label: t('earn.report.stats.earned_total'), value: earnedTotal }, @@ -212,23 +212,46 @@ export const EarnReportContent = ({ className, enabled }: EarnReportContentProps {/* Toolbar: search + refresh */} -
-
- - table.setGlobalFilter(event.target.value)} - className="pl-8" - /> +
+
+ + table.setGlobalFilter(event.target.value)} + placeholder={t('earn.report.placeholder_search')} + /> + + + + + + + + + +
- - {globalFilter === '' @@ -236,7 +259,7 @@ export const EarnReportContent = ({ className, enabled }: EarnReportContentProps : t('earn.report.text_report_summary_filtered', { count: table.getFilteredRowModel().rows.length })} {isDeveloperMode ? ( -
{stacktrace && (
{t('error_page.heading_stacktrace')}
-
+          
             {stacktrace}
           
diff --git a/src/components/import/ImportDetailsForm.test.tsx b/src/components/import/ImportDetailsForm.test.tsx index f6ca64c5..1dd18e8b 100644 --- a/src/components/import/ImportDetailsForm.test.tsx +++ b/src/components/import/ImportDetailsForm.test.tsx @@ -1,7 +1,11 @@ import type { ReactNode } from 'react' +import type { SessionResponse } from '@joinmarket-webui/joinmarket-ng-api-ts/jm' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' +import { DUMMY_SEED_PHRASE, pseudoRandomInteger } from '@/lib/utils' import { flushActUpdates } from '@/test/flushActUpdates' +import { withRuntimeLocale } from '@/test/withRuntimeLocale' +import type { BlockHeight } from '@/types/global' import { ImportDetailsForm } from './ImportDetailsForm' vi.mock('react-i18next', () => ({ @@ -10,8 +14,6 @@ vi.mock('react-i18next', () => ({ }), })) -vi.mock('../dev/DevBadge', () => ({ DevBadge: () => })) - vi.mock('../ui/accordion', () => ({ Accordion: ({ children }: { children?: ReactNode }) =>
{children}
, AccordionItem: ({ children }: { children?: ReactNode }) =>
{children}
, @@ -19,95 +21,79 @@ vi.mock('../ui/accordion', () => ({ AccordionContent: ({ children }: { children?: ReactNode }) =>
{children}
, })) -vi.mock('../ui/alert', () => ({ - Alert: ({ children, variant }: { children?: ReactNode; variant?: string }) => ( -
{children}
- ), - AlertDescription: ({ children }: { children?: ReactNode }) =>
{children}
, - AlertTitle: ({ children }: { children?: ReactNode }) =>
{children}
, -})) - -vi.mock('@/components/ui/field', () => ({ - Field: ({ children }: { children?: ReactNode }) =>
{children}
, - FieldDescription: ({ children }: { children?: ReactNode }) =>
{children}
, - FieldLabel: ({ children }: { children?: ReactNode }) => , -})) - -vi.mock('../ui/input-group', () => ({ - InputGroup: ({ children }: { children?: ReactNode }) =>
{children}
, - InputGroupAddon: ({ children }: { children?: ReactNode }) =>
{children}
, - InputGroupInput: (props: Record) => , -})) - -vi.mock('../ui/textarea', () => ({ - Textarea: (props: Record) =>