diff --git a/packages/extension/package.json b/packages/extension/package.json index 16516e279..08a20505d 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -18,6 +18,7 @@ "@mui/icons-material": "^6.3.0", "@mui/material": "^6.3.0", "@sentry/react": "^7.117.0", + "@tanstack/react-query": "^5.90.12", "copy-to-clipboard": "^3.3.3", "crypto-browserify": "^3.12.0", "crypto-js": "^4.2.0", diff --git a/packages/extension/src/components/organisms/TokenListing/TokenListing.test.tsx b/packages/extension/src/components/organisms/TokenListing/TokenListing.test.tsx index 3390ee715..847e7b0c2 100644 --- a/packages/extension/src/components/organisms/TokenListing/TokenListing.test.tsx +++ b/packages/extension/src/components/organisms/TokenListing/TokenListing.test.tsx @@ -1,13 +1,13 @@ -import * as Sentry from '@sentry/react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Chain, XRPLNetwork } from '@gemwallet/constants'; import { DEFAULT_RESERVE, RESERVE_PER_OWNER } from '../../../constants'; import { formatToken } from '../../../utils'; import { TokenListing, TokenListingProps } from './TokenListing'; -import { vi, Mock } from 'vitest'; +import { vi, Mock, describe, test, expect, beforeEach } from 'vitest'; const user = userEvent.setup(); @@ -21,6 +21,7 @@ vi.mock('@sentry/react', () => { const mockGetBalancesPromise = vi.fn(); const mockFundWalletPromise = vi.fn(); const mockRequestPromise = vi.fn(); +const mockGetAccountInfo = vi.fn(); const mockChain = Chain.XRPL; let mockNetwork = XRPLNetwork.TESTNET; @@ -29,16 +30,6 @@ let mockClient: { getBalances: Mock; request: Mock } | null = { request: mockRequestPromise }; -mockGetBalancesPromise.mockResolvedValueOnce([ - { value: '100', currency: 'XRP', issuer: undefined } -]); - -mockRequestPromise.mockResolvedValueOnce({ - result: { - lines: [] - } -}); - vi.mock('../../../contexts', () => { return { useNetwork: () => ({ @@ -59,22 +50,32 @@ vi.mock('../../../contexts', () => { }), useLedger: () => ({ fundWallet: mockFundWalletPromise, - getAccountInfo: vi.fn().mockImplementation(() => - Promise.resolve({ - result: { - account_data: { - OwnerCount: 2 - } - } - }) - ) + getAccountInfo: mockGetAccountInfo }) }; }); +// Create a wrapper component with QueryClientProvider for testing +const createTestQueryClient = () => + new QueryClient({ + defaultOptions: { + queries: { + retry: false, + gcTime: 0 + } + } + }); + +const renderWithQueryClient = (component: React.ReactElement) => { + const queryClient = createTestQueryClient(); + return render({component}); +}; + describe('TokenListing', () => { let props: TokenListingProps; + beforeEach(() => { + vi.clearAllMocks(); mockClient = { getBalances: mockGetBalancesPromise, request: mockRequestPromise @@ -82,11 +83,24 @@ describe('TokenListing', () => { props = { address: 'r123' }; + // Default mock responses + mockRequestPromise.mockResolvedValue({ + result: { + lines: [] + } + }); + mockGetAccountInfo.mockResolvedValue({ + result: { + account_data: { + OwnerCount: 2 + } + } + }); }); test('should display an error when client failed to load', () => { mockClient = null; - render(); + renderWithQueryClient(); expect( screen.queryByText( 'There was an error attempting to connect to the network. Please refresh the page and try again.' @@ -95,12 +109,13 @@ describe('TokenListing', () => { }); test('should display the loading token state when the XRPBalance is not calculated', () => { - render(); + mockGetBalancesPromise.mockReturnValue(new Promise(() => {})); // Never resolves + renderWithQueryClient(); expect(screen.getByTestId('token-loader')).toBeInTheDocument(); }); test('should display the XRP balance and trust line balances', async () => { - mockGetBalancesPromise.mockResolvedValueOnce([ + mockGetBalancesPromise.mockResolvedValue([ { value: '100', currency: 'XRP', issuer: undefined }, { value: '50', currency: 'USD', issuer: 'r123' }, { value: '20', currency: 'ETH', issuer: 'r456' } @@ -108,7 +123,7 @@ describe('TokenListing', () => { const reserve = DEFAULT_RESERVE + RESERVE_PER_OWNER * 2; - render(); + renderWithQueryClient(); await waitFor(() => { expect(screen.getByText(`${100 - reserve} XRP`)).toBeInTheDocument(); expect(screen.getByText('50 USD')).toBeInTheDocument(); @@ -117,21 +132,20 @@ describe('TokenListing', () => { }); test('should display an error message when there is an error fetching the balances', async () => { - mockGetBalancesPromise.mockRejectedValueOnce( + mockGetBalancesPromise.mockRejectedValue( new Error('Throw an error if there is an error fetching the balances') ); - render(); + renderWithQueryClient(); await waitFor(() => { expect(screen.getByText('Account not activated')).toBeVisible(); - expect(Sentry.captureException).toHaveBeenCalled(); }); }); test('should open the explanation dialog when the explain button is clicked', async () => { - mockGetBalancesPromise.mockResolvedValueOnce([ + mockGetBalancesPromise.mockResolvedValue([ { value: '100', currency: 'XRP', issuer: undefined } ]); - render(); + renderWithQueryClient(); const explainButton = await screen.findByText('Explain'); await user.click(explainButton); expect( @@ -143,10 +157,10 @@ describe('TokenListing', () => { test('Should display the fund wallet button when the network is testnet and XRP balance is 0', async () => { mockNetwork = XRPLNetwork.TESTNET; - mockGetBalancesPromise.mockRejectedValueOnce( + mockGetBalancesPromise.mockRejectedValue( new Error('Throw an error if there is an error fetching the balances') ); - render(); + renderWithQueryClient(); await waitFor(() => { const button = screen.queryByTestId('fund-wallet-button'); expect(button).toBeInTheDocument(); @@ -155,25 +169,31 @@ describe('TokenListing', () => { test('Should not display the fund wallet button when the network is Mainnet and XRP balance is 0', async () => { mockNetwork = XRPLNetwork.MAINNET; - mockGetBalancesPromise.mockRejectedValueOnce( + mockGetBalancesPromise.mockRejectedValue( new Error('Throw an error if there is an error fetching the balances') ); - render(); + renderWithQueryClient(); await waitFor(() => { const button = screen.queryByTestId('fund-wallet-button'); expect(button).not.toBeInTheDocument(); }); }); - test('Should display the amount of XRP when click on Fund Wallet Button', async () => { + test('Should refetch balances when clicking Fund Wallet Button', async () => { const reserve = DEFAULT_RESERVE + RESERVE_PER_OWNER * 2; mockNetwork = XRPLNetwork.TESTNET; - mockFundWalletPromise.mockResolvedValueOnce({ balance: 10000 }); + // First call fails (account not activated) mockGetBalancesPromise.mockRejectedValueOnce( new Error('Throw an error if there is an error fetching the balances') ); - render(); + // After funding, return balances + mockGetBalancesPromise.mockResolvedValue([ + { value: '10000', currency: 'XRP', issuer: undefined } + ]); + mockFundWalletPromise.mockResolvedValue({ balance: 10000 }); + + renderWithQueryClient(); const button = await screen.findByTestId('fund-wallet-button'); const format = formatToken(10000 - reserve, 'XRP'); diff --git a/packages/extension/src/components/organisms/TokenListing/TokenListing.tsx b/packages/extension/src/components/organisms/TokenListing/TokenListing.tsx index ddd211055..a97332cc9 100644 --- a/packages/extension/src/components/organisms/TokenListing/TokenListing.tsx +++ b/packages/extension/src/components/organisms/TokenListing/TokenListing.tsx @@ -1,9 +1,8 @@ -import { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { FC, useCallback, useMemo, useState } from 'react'; import { Button, Link, Typography } from '@mui/material'; -import * as Sentry from '@sentry/react'; +import { useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; -import { AccountLinesTrustline } from 'xrpl'; import { TrustSetFlags as TrustSetFlagsBitmask } from 'xrpl'; import { Chain, XahauNetwork, XRPLNetwork } from '@gemwallet/constants'; @@ -12,12 +11,10 @@ import { ADD_NEW_TRUSTLINE_PATH, DEFAULT_RESERVE, ERROR_RED, - RESERVE_PER_OWNER, - STORAGE_MESSAGING_KEY, - XAHAU_RESERVE_PER_OWNER + STORAGE_MESSAGING_KEY } from '../../../constants'; import { useLedger, useNetwork, useServer } from '../../../contexts'; -import { useMainToken } from '../../../hooks'; +import { useAccountBalances, accountQueryKeys, useMainToken } from '../../../hooks'; import { convertHexCurrencyString, generateKey, saveInChromeSessionStorage } from '../../../utils'; import { isLPToken } from '../../../utils/trustlines'; import { TokenLoader } from '../../atoms'; @@ -25,95 +22,32 @@ import { InformationMessage } from '../../molecules/InformationMessage'; import { TokenDisplay } from '../../molecules/TokenDisplay'; import { DialogPage } from '../../templates'; -const LOADING_STATE = 'Loading...'; -const ERROR_STATE = 'Error'; - -interface TrustLineBalance { - value: string; - currency: string; - issuer: string; - trustlineDetails?: { - // Details need to be fetched with a separate call - limit: number; - noRipple: boolean; - }; -} export interface TokenListingProps { address: string; } export const TokenListing: FC = ({ address }) => { - const [mainTokenBalance, setMainTokenBalance] = useState(LOADING_STATE); - const [reserve, setReserve] = useState(DEFAULT_RESERVE); - const [ownerReserve, setOwnerReserve] = useState(0); const [errorMessage, setErrorMessage] = useState(''); - const [trustLineBalances, setTrustLineBalances] = useState([]); const [explanationOpen, setExplanationOpen] = useState(false); + const [isFunding, setIsFunding] = useState(false); const { client, reconnectToNetwork, networkName, chainName } = useNetwork(); const { serverInfo } = useServer(); - const { fundWallet, getAccountInfo } = useLedger(); + const { fundWallet } = useLedger(); const mainToken = useMainToken(); const navigate = useNavigate(); + const queryClient = useQueryClient(); - useEffect(() => { - async function fetchBalance() { - try { - // Retrieve balances without trustline details - const balances = await client?.getBalances(address); - const mainTokenBalance = balances?.find((balance) => balance.issuer === undefined); - let trustLineBalances = balances?.filter( - (balance) => balance.issuer !== undefined - ) as TrustLineBalance[]; - - // Retrieve trustlines details - const accountLines = await client?.request({ - command: 'account_lines', - account: address - }); - - if (accountLines?.result?.lines) { - trustLineBalances = trustLineBalances - .map((trustlineBalance) => { - const trustlineDetails = accountLines.result.lines.find( - (line: AccountLinesTrustline) => - line.currency === trustlineBalance.currency && - line.account === trustlineBalance.issuer - ); + // Use cached account balances - data persists across page navigations + const { data: accountData, isLoading, isError } = useAccountBalances(address); - return { - ...trustlineBalance, - trustlineDetails: - trustlineDetails && Number(trustlineDetails.limit) - ? { - limit: Number(trustlineDetails.limit), - noRipple: trustlineDetails.no_ripple === true - } - : undefined - }; - }) - .filter( - (trustlineBalance) => - trustlineBalance.trustlineDetails || trustlineBalance.value !== '0' - ); // Hide revoked trustlines with a balance of 0 - } - - if (mainTokenBalance) { - setMainTokenBalance(mainTokenBalance.value); - } - if (trustLineBalances) { - setTrustLineBalances(trustLineBalances); - } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e: any) { - if (e?.data?.error !== 'actNotFound') { - Sentry.captureException(e); - } - setMainTokenBalance(ERROR_STATE); - } - } - - fetchBalance(); - }, [address, client]); + const mainTokenBalance = accountData?.mainTokenBalance || '0'; + const trustLineBalances = accountData?.trustLineBalances || []; + const reserve = accountData?.reserve || DEFAULT_RESERVE; + const ownerReserve = accountData?.ownerReserve || 0; + const baseReserve = + accountData?.baseReserve || + serverInfo?.info.validated_ledger?.reserve_base_xrp || + DEFAULT_RESERVE; const handleOpen = useCallback(() => { setExplanationOpen(true); @@ -136,14 +70,21 @@ export const TokenListing: FC = ({ address }) => { const handleFundWallet = useCallback(() => { setErrorMessage(''); - setMainTokenBalance(LOADING_STATE); + setIsFunding(true); fundWallet() - .then(({ balance }) => setMainTokenBalance(balance.toString())) + .then(() => { + // Invalidate the cache to refetch fresh balances + queryClient.invalidateQueries({ + queryKey: accountQueryKeys.balances(address, networkName) + }); + }) .catch((e) => { - setMainTokenBalance(ERROR_STATE); setErrorMessage(e.message); + }) + .finally(() => { + setIsFunding(false); }); - }, [fundWallet]); + }, [fundWallet, queryClient, address, networkName]); if (client === null) { return ( @@ -167,26 +108,11 @@ export const TokenListing: FC = ({ address }) => { ); } - if (mainTokenBalance === LOADING_STATE) { + if (isLoading || isFunding) { return ; } - const baseReserve = serverInfo?.info.validated_ledger?.reserve_base_xrp || DEFAULT_RESERVE; - const ownerReserveBase = - chainName === Chain.XAHAU - ? serverInfo?.info.validated_ledger?.reserve_inc_xrp || XAHAU_RESERVE_PER_OWNER - : serverInfo?.info.validated_ledger?.reserve_inc_xrp || RESERVE_PER_OWNER; - getAccountInfo() - .then((accountInfo) => { - const ownerReserve = accountInfo.result.account_data.OwnerCount * ownerReserveBase; - setOwnerReserve(ownerReserve); - setReserve(ownerReserve + baseReserve); - }) - .catch(() => { - setOwnerReserve(0); - setReserve(DEFAULT_RESERVE); - }); - if (mainTokenBalance === ERROR_STATE) { + if (isError) { return (
diff --git a/packages/extension/src/contexts/QueryContext/QueryContext.tsx b/packages/extension/src/contexts/QueryContext/QueryContext.tsx new file mode 100644 index 000000000..b1081919e --- /dev/null +++ b/packages/extension/src/contexts/QueryContext/QueryContext.tsx @@ -0,0 +1,30 @@ +import { FC, ReactNode } from 'react'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +// Configure QueryClient with sensible defaults for a wallet extension +// - staleTime: 30 seconds - data is fresh for 30s, no refetch needed +// - gcTime: 5 minutes - cached data kept for 5min after component unmounts +// - refetchOnWindowFocus: false - don't refetch when popup regains focus +// - retry: 1 - retry failed requests once +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 30 * 1000, // 30 seconds + gcTime: 5 * 60 * 1000, // 5 minutes (garbage collection time) + refetchOnWindowFocus: false, + retry: 1, + refetchOnMount: false // Don't refetch if data is still fresh + } + } +}); + +interface Props { + children: ReactNode; +} + +export const QueryProvider: FC = ({ children }) => { + return {children}; +}; + +export { queryClient }; diff --git a/packages/extension/src/contexts/QueryContext/index.ts b/packages/extension/src/contexts/QueryContext/index.ts new file mode 100644 index 000000000..24b920b6b --- /dev/null +++ b/packages/extension/src/contexts/QueryContext/index.ts @@ -0,0 +1 @@ +export * from './QueryContext'; diff --git a/packages/extension/src/contexts/index.ts b/packages/extension/src/contexts/index.ts index bd72036bd..2957e3960 100644 --- a/packages/extension/src/contexts/index.ts +++ b/packages/extension/src/contexts/index.ts @@ -2,6 +2,7 @@ export * from './BrowserContext'; export * from './LedgerContext'; export * from './NavBarContext'; export * from './NetworkContext'; +export * from './QueryContext'; export * from './ServerContext'; export * from './TransactionProgressContext'; export * from './WalletContext'; diff --git a/packages/extension/src/hooks/index.ts b/packages/extension/src/hooks/index.ts index 883e14e08..7cb40f899 100644 --- a/packages/extension/src/hooks/index.ts +++ b/packages/extension/src/hooks/index.ts @@ -1,3 +1,4 @@ +export * from './useAccountData'; export * from './useBeforeUnload'; export * from './useFees'; export * from './useFetchFromSessionStorage'; diff --git a/packages/extension/src/hooks/useAccountData/index.ts b/packages/extension/src/hooks/useAccountData/index.ts new file mode 100644 index 000000000..9f998aa0f --- /dev/null +++ b/packages/extension/src/hooks/useAccountData/index.ts @@ -0,0 +1 @@ +export * from './useAccountData'; diff --git a/packages/extension/src/hooks/useAccountData/useAccountData.ts b/packages/extension/src/hooks/useAccountData/useAccountData.ts new file mode 100644 index 000000000..1797ae458 --- /dev/null +++ b/packages/extension/src/hooks/useAccountData/useAccountData.ts @@ -0,0 +1,137 @@ +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { AccountLinesTrustline } from 'xrpl'; + +import { useNetwork, useLedger, useServer } from '../../contexts'; +import { Chain } from '@gemwallet/constants'; +import { DEFAULT_RESERVE, RESERVE_PER_OWNER, XAHAU_RESERVE_PER_OWNER } from '../../constants'; + +interface TrustLineBalance { + value: string; + currency: string; + issuer: string; + trustlineDetails?: { + limit: number; + noRipple: boolean; + }; +} + +interface AccountBalanceData { + mainTokenBalance: string; + trustLineBalances: TrustLineBalance[]; + reserve: number; + ownerReserve: number; + baseReserve: number; +} + +// Query keys for cache management +export const accountQueryKeys = { + all: ['account'] as const, + balances: (address: string, networkName: string) => + [...accountQueryKeys.all, 'balances', address, networkName] as const, + accountInfo: (address: string, networkName: string) => + [...accountQueryKeys.all, 'info', address, networkName] as const +}; + +/** + * Hook to fetch and cache account balances with TanStack Query. + * Data is cached for 30 seconds and won't refetch on page navigation. + */ +export const useAccountBalances = (address: string) => { + const { client, networkName, chainName } = useNetwork(); + const { serverInfo } = useServer(); + const { getAccountInfo } = useLedger(); + + const baseReserve = serverInfo?.info.validated_ledger?.reserve_base_xrp || DEFAULT_RESERVE; + const ownerReserveBase = + chainName === Chain.XAHAU + ? serverInfo?.info.validated_ledger?.reserve_inc_xrp || XAHAU_RESERVE_PER_OWNER + : serverInfo?.info.validated_ledger?.reserve_inc_xrp || RESERVE_PER_OWNER; + + return useQuery({ + queryKey: accountQueryKeys.balances(address, networkName), + queryFn: async (): Promise => { + if (!client) { + throw new Error('Client not connected'); + } + + // Fetch balances + const balances = await client.getBalances(address); + const mainTokenBalance = balances?.find((balance) => balance.issuer === undefined); + let trustLineBalances = balances?.filter( + (balance) => balance.issuer !== undefined + ) as TrustLineBalance[]; + + // Fetch account lines for trustline details + const accountLines = await client.request({ + command: 'account_lines', + account: address + }); + + if (accountLines?.result?.lines) { + trustLineBalances = trustLineBalances + .map((trustlineBalance) => { + const trustlineDetails = accountLines.result.lines.find( + (line: AccountLinesTrustline) => + line.currency === trustlineBalance.currency && + line.account === trustlineBalance.issuer + ); + + return { + ...trustlineBalance, + trustlineDetails: + trustlineDetails && Number(trustlineDetails.limit) + ? { + limit: Number(trustlineDetails.limit), + noRipple: trustlineDetails.no_ripple === true + } + : undefined + }; + }) + .filter( + (trustlineBalance) => + trustlineBalance.trustlineDetails || trustlineBalance.value !== '0' + ); + } + + // Fetch account info for reserve calculation + const accountInfo = await getAccountInfo(); + const calculatedOwnerReserve = accountInfo.result.account_data.OwnerCount * ownerReserveBase; + const reserve = calculatedOwnerReserve + baseReserve; + + return { + mainTokenBalance: mainTokenBalance?.value || '0', + trustLineBalances: trustLineBalances || [], + reserve, + ownerReserve: calculatedOwnerReserve, + baseReserve + }; + }, + enabled: !!client && !!address, + staleTime: 30 * 1000, // Data is fresh for 30 seconds + gcTime: 5 * 60 * 1000, // Keep in cache for 5 minutes + refetchOnMount: false, // Don't refetch when component mounts if data is fresh + refetchOnWindowFocus: false + // retry is inherited from QueryClient defaults (false in tests, 1 in production) + }); +}; + +/** + * Hook to invalidate account balance cache. + * Call this after transactions that change balances. + */ +export const useInvalidateAccountBalances = () => { + const queryClient = useQueryClient(); + + return (address?: string, networkName?: string) => { + if (address && networkName) { + queryClient.invalidateQueries({ + queryKey: accountQueryKeys.balances(address, networkName) + }); + } else { + // Invalidate all account balance queries + queryClient.invalidateQueries({ + queryKey: [...accountQueryKeys.all, 'balances'] + }); + } + }; +}; diff --git a/packages/extension/src/index.tsx b/packages/extension/src/index.tsx index 11c304d68..0e360a4a8 100644 --- a/packages/extension/src/index.tsx +++ b/packages/extension/src/index.tsx @@ -19,6 +19,7 @@ import { LedgerProvider, NavBarPositionProvider, NetworkProvider, + QueryProvider, ServerProvider, TransactionProgressProvider, WalletProvider @@ -87,23 +88,25 @@ const GemWallet = () => { - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/yarn.lock b/yarn.lock index 620afd8c5..623277fa5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1232,81 +1232,206 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5" integrity sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w== +"@rollup/rollup-android-arm-eabi@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz#76e0fef6533b3ce313f969879e61e8f21f0eeb28" + integrity sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg== + "@rollup/rollup-android-arm64@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz#654ca1049189132ff602bfcf8df14c18da1f15fb" integrity sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA== +"@rollup/rollup-android-arm64@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz#d3cfc675a40bbdec97bda6d7fe3b3b05f0e1cd93" + integrity sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg== + "@rollup/rollup-darwin-arm64@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz#6d241d099d1518ef0c2205d96b3fa52e0fe1954b" integrity sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q== +"@rollup/rollup-darwin-arm64@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz#eb912b8f59dd47c77b3c50a78489013b1d6772b4" + integrity sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg== + "@rollup/rollup-darwin-x64@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz#42bd19d292a57ee11734c980c4650de26b457791" integrity sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw== +"@rollup/rollup-darwin-x64@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz#e7d0839fdfd1276a1d34bc5ebbbd0dfd7d0b81a0" + integrity sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ== + +"@rollup/rollup-freebsd-arm64@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz#7ff8118760f7351e48fd0cd3717ff80543d6aac8" + integrity sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg== + +"@rollup/rollup-freebsd-x64@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz#49d330dadbda1d4e9b86b4a3951b59928a9489a9" + integrity sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw== + "@rollup/rollup-linux-arm-gnueabihf@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz#f23555ee3d8fe941c5c5fd458cd22b65eb1c2232" integrity sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ== +"@rollup/rollup-linux-arm-gnueabihf@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz#98c5f1f8b9776b4a36e466e2a1c9ed1ba52ef1b6" + integrity sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ== + "@rollup/rollup-linux-arm-musleabihf@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz#f3bbd1ae2420f5539d40ac1fde2b38da67779baa" integrity sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg== +"@rollup/rollup-linux-arm-musleabihf@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz#b9acecd3672e742f70b0c8a94075c816a91ff040" + integrity sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg== + "@rollup/rollup-linux-arm64-gnu@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz#7abe900120113e08a1f90afb84c7c28774054d15" integrity sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw== +"@rollup/rollup-linux-arm64-gnu@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz#7a6ab06651bc29e18b09a50ed1a02bc972977c9b" + integrity sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ== + "@rollup/rollup-linux-arm64-musl@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz#9e655285c8175cd44f57d6a1e8e5dedfbba1d820" integrity sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA== +"@rollup/rollup-linux-arm64-musl@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz#3c8c9072ba4a4d4ef1156b85ab9a2cbb57c1fad0" + integrity sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA== + +"@rollup/rollup-linux-loong64-gnu@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz#17a7af13530f4e4a7b12cd26276c54307a84a8b0" + integrity sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g== + +"@rollup/rollup-linux-loong64-musl@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz#5cd7a900fd7b077ecd753e34a9b7ff1157fe70c1" + integrity sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw== + "@rollup/rollup-linux-powerpc64le-gnu@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz#9a79ae6c9e9d8fe83d49e2712ecf4302db5bef5e" integrity sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg== +"@rollup/rollup-linux-ppc64-gnu@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz#03a097e70243ddf1c07b59d3c20f38e6f6800539" + integrity sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw== + +"@rollup/rollup-linux-ppc64-musl@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz#a5389873039d4650f35b4fa060d286392eb21a94" + integrity sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw== + "@rollup/rollup-linux-riscv64-gnu@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz#67ac70eca4ace8e2942fabca95164e8874ab8128" integrity sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA== +"@rollup/rollup-linux-riscv64-gnu@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz#789e60e7d6e2b76132d001ffb24ba80007fb17d0" + integrity sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw== + +"@rollup/rollup-linux-riscv64-musl@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz#3556fa88d139282e9a73c337c9a170f3c5fe7aa4" + integrity sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg== + "@rollup/rollup-linux-s390x-gnu@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz#9f883a7440f51a22ed7f99e1d070bd84ea5005fc" integrity sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q== +"@rollup/rollup-linux-s390x-gnu@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz#c085995b10143c16747a67f1a5487512b2ff04b2" + integrity sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg== + "@rollup/rollup-linux-x64-gnu@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz#70116ae6c577fe367f58559e2cffb5641a1dd9d0" integrity sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg== +"@rollup/rollup-linux-x64-gnu@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz#9563a5419dd2604841bad31a39ccfdd2891690fb" + integrity sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg== + "@rollup/rollup-linux-x64-musl@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz#f473f88219feb07b0b98b53a7923be716d1d182f" integrity sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g== +"@rollup/rollup-linux-x64-musl@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz#691bb06e6269a8959c13476b0cd2aa7458facb31" + integrity sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w== + +"@rollup/rollup-openbsd-x64@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz#223e71224746a59ce6d955bbc403577bb5a8be9d" + integrity sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg== + +"@rollup/rollup-openharmony-arm64@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz#0817e5d8ecbfeb8b7939bf58f8ce3c9dd67fce77" + integrity sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw== + "@rollup/rollup-win32-arm64-msvc@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz#4349482d17f5d1c58604d1c8900540d676f420e0" integrity sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw== +"@rollup/rollup-win32-arm64-msvc@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz#de56d8f2013c84570ef5fb917aae034abda93e4a" + integrity sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g== + "@rollup/rollup-win32-ia32-msvc@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz#a6fc39a15db618040ec3c2a24c1e26cb5f4d7422" integrity sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g== +"@rollup/rollup-win32-ia32-msvc@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz#659aff5244312475aeea2c9479a6c7d397b517bf" + integrity sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA== + +"@rollup/rollup-win32-x64-gnu@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz#2cb09549cbb66c1b979f9238db6dd454cac14a88" + integrity sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg== + "@rollup/rollup-win32-x64-msvc@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz#3dd5d53e900df2a40841882c02e56f866c04d202" integrity sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q== +"@rollup/rollup-win32-x64-msvc@4.55.1": + version "4.55.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz#f79437939020b83057faf07e98365b1fa51c458b" + integrity sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw== + "@scure/base@^1.1.3", "@scure/base@~1.1.4": version "1.1.5" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.5.tgz#1d85d17269fe97694b9c592552dd9e5e33552157" @@ -1472,6 +1597,18 @@ resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== +"@tanstack/query-core@5.90.16": + version "5.90.16" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.90.16.tgz#19a972c2ffbc47727ab6649028af1bee70e28cdf" + integrity sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww== + +"@tanstack/react-query@^5.90.12": + version "5.90.16" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.90.16.tgz#76955d7027b5bff3d7f51163db7ea1c0bd430cb5" + integrity sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ== + dependencies: + "@tanstack/query-core" "5.90.16" + "@testing-library/dom@^9.0.0": version "9.3.4" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.3.4.tgz#50696ec28376926fec0a1bf87d9dbac5e27f60ce" @@ -1612,6 +1749,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": version "4.17.43" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz#10d8444be560cb789c4735aea5eac6e5af45df54" @@ -6681,6 +6823,11 @@ nan@^2.13.2: resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + nanoid@^3.3.7: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -7119,6 +7266,11 @@ picocolors@^1.0.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -7188,6 +7340,15 @@ postcss@^8.4.39: picocolors "^1.0.1" source-map-js "^1.2.0" +postcss@^8.4.43: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -7805,6 +7966,40 @@ rollup@^4.13.0: "@rollup/rollup-win32-x64-msvc" "4.22.4" fsevents "~2.3.2" +rollup@^4.20.0: + version "4.55.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.55.1.tgz#4ec182828be440648e7ee6520dc35e9f20e05144" + integrity sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.55.1" + "@rollup/rollup-android-arm64" "4.55.1" + "@rollup/rollup-darwin-arm64" "4.55.1" + "@rollup/rollup-darwin-x64" "4.55.1" + "@rollup/rollup-freebsd-arm64" "4.55.1" + "@rollup/rollup-freebsd-x64" "4.55.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.55.1" + "@rollup/rollup-linux-arm-musleabihf" "4.55.1" + "@rollup/rollup-linux-arm64-gnu" "4.55.1" + "@rollup/rollup-linux-arm64-musl" "4.55.1" + "@rollup/rollup-linux-loong64-gnu" "4.55.1" + "@rollup/rollup-linux-loong64-musl" "4.55.1" + "@rollup/rollup-linux-ppc64-gnu" "4.55.1" + "@rollup/rollup-linux-ppc64-musl" "4.55.1" + "@rollup/rollup-linux-riscv64-gnu" "4.55.1" + "@rollup/rollup-linux-riscv64-musl" "4.55.1" + "@rollup/rollup-linux-s390x-gnu" "4.55.1" + "@rollup/rollup-linux-x64-gnu" "4.55.1" + "@rollup/rollup-linux-x64-musl" "4.55.1" + "@rollup/rollup-openbsd-x64" "4.55.1" + "@rollup/rollup-openharmony-arm64" "4.55.1" + "@rollup/rollup-win32-arm64-msvc" "4.55.1" + "@rollup/rollup-win32-ia32-msvc" "4.55.1" + "@rollup/rollup-win32-x64-gnu" "4.55.1" + "@rollup/rollup-win32-x64-msvc" "4.55.1" + fsevents "~2.3.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -8111,6 +8306,11 @@ source-map-js@^1.2.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -8842,7 +9042,7 @@ vite-plugin-node-polyfills@^0.19.0: "@rollup/plugin-inject" "^5.0.5" node-stdlib-browser "^1.2.0" -vite@^5.0.0, vite@^5.3.6: +vite@^5.0.0: version "5.3.6" resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.6.tgz#e097c0a7b79adb2e60bec9ef7907354f09d027bd" integrity sha512-es78AlrylO8mTVBygC0gTC0FENv0C6T496vvd33ydbjF/mIi9q3XQ9A3NWo5qLGFKywvz10J26813OkLvcQleA== @@ -8853,6 +9053,17 @@ vite@^5.0.0, vite@^5.3.6: optionalDependencies: fsevents "~2.3.3" +vite@^5.4.18: + version "5.4.21" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.21.tgz#84a4f7c5d860b071676d39ba513c0d598fdc7027" + integrity sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + vitest-canvas-mock@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/vitest-canvas-mock/-/vitest-canvas-mock-0.3.3.tgz#97e3b5f53003c5cbb9540204ff3122cd25be4dcd"