Delegation query cache, log webview streaming, HTTPS webviews

- Use_https_scheme(true) on log window builder
- Delegation data is loaded and refreshed via TanStack Query
This commit is contained in:
Tommy Verrall
2026-05-06 15:19:13 +02:00
parent 78b796bf24
commit 09548a9aa9
15 changed files with 766 additions and 214 deletions
+3 -2
View File
@@ -27,6 +27,7 @@
},
"dependencies": {
"@babel/helper-simple-access": "^7.25.9",
"@emotion/cache": "^11.14.0",
"@emotion/react": "^11.7.0",
"@emotion/styled": "^11.6.0",
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
@@ -38,6 +39,7 @@
"@nymproject/node-tester": "^1.3.1",
"@nymproject/react": "^1.0.0",
"@nymproject/types": "^1.0.0",
"@tanstack/react-query": "^5.62.0",
"@tauri-apps/api": "^2.10.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-opener": "^2.5.3",
@@ -46,7 +48,6 @@
"@tauri-apps/tauri-forage": "^1.0.0-beta.2",
"big.js": "^6.2.1",
"bs58": "^4.0.1",
"@emotion/cache": "^11.14.0",
"clsx": "^1.1.1",
"date-fns": "^2.28.0",
"joi": "^17.11.0",
@@ -138,4 +139,4 @@
"@types/minimatch": "5.1.2"
},
"private": false
}
}
+10 -3
View File
@@ -62,9 +62,16 @@ pub fn setup_logging(app_handle: tauri::AppHandle) -> Result<(), log::SetLoggerE
message: record.args().to_string(),
level: record.level().into(),
};
// Tauri 2: target the log webview explicitly so the dedicated window receives events.
if let Some(log_win) = log_window_app.get_webview_window("log") {
let _ = log_win.emit("log://log", msg);
let app = log_window_app.clone();
let app_for_emit = app.clone();
if let Err(e) = app.run_on_main_thread(move || {
if let Some(log_win) = app_for_emit.get_webview_window("log") {
if let Err(err) = log_win.emit("log://log", msg) {
log::warn!("failed to emit log line to log webview: {err}");
}
}
}) {
log::warn!("failed to schedule log line for log webview: {e}");
}
}));
@@ -34,6 +34,7 @@ async fn create_window(
)
.title("Nym Wallet")
.background_color(NYM_WALLET_WEBVIEW_BG)
.use_https_scheme(true)
.build()
{
Ok(window) => {
@@ -1,5 +1,6 @@
use crate::error::BackendError;
use crate::webview_theme::NYM_WALLET_WEBVIEW_BG;
use tauri::webview::PageLoadEvent;
use tauri::Manager;
#[tauri::command]
@@ -20,6 +21,22 @@ pub fn help_log_toggle_window(app_handle: tauri::AppHandle) -> Result<(), Backen
)
.title("Nym Wallet Logs")
.background_color(NYM_WALLET_WEBVIEW_BG)
.use_https_scheme(true)
.on_page_load(|window, payload| match payload.event() {
PageLoadEvent::Started => {
log::debug!("Log webview load started: {}", payload.url());
}
PageLoadEvent::Finished => {
log::info!("Log webview load finished: {}", payload.url());
if std::env::var("NYM_WALLET_LOG_WEBVIEW_DEVTOOLS")
.ok()
.as_deref()
== Some("1")
{
window.open_devtools();
}
}
})
.build()
{
Ok(window) => {
+24 -12
View File
@@ -1,4 +1,5 @@
import React, { ComponentType, useEffect } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ErrorBoundary } from 'react-error-boundary';
import { BrowserRouter, HashRouter } from 'react-router-dom';
import { SnackbarProvider } from 'notistack';
@@ -11,6 +12,15 @@ import { useTauriTextEditingClipboard } from './hooks/useTauriTextEditingClipboa
type RouterComponent = ComponentType<{ children?: React.ReactNode }>;
const walletQueryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 1,
refetchOnWindowFocus: false,
},
},
});
const ClipboardBridge: FCWithChildren = ({ children }) => {
useTauriTextEditingClipboard();
return children;
@@ -48,18 +58,20 @@ export const AppCommon = ({
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Router>
<SnackbarProvider
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
>
<AppProvider>
<NymWalletTheme>
<ClipboardBridge>{children}</ClipboardBridge>
</NymWalletTheme>
</AppProvider>
</SnackbarProvider>
<QueryClientProvider client={walletQueryClient}>
<SnackbarProvider
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
>
<AppProvider>
<NymWalletTheme>
<ClipboardBridge>{children}</ClipboardBridge>
</NymWalletTheme>
</AppProvider>
</SnackbarProvider>
</QueryClientProvider>
</Router>
</ErrorBoundary>
);
+129 -48
View File
@@ -3,16 +3,24 @@ import type { UnlistenFn } from '@tauri-apps/api/event';
import { getCurrentWebview } from '@tauri-apps/api/webview';
import {
Box,
Paper,
Button,
Chip,
Paper,
Stack,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
useTheme,
} from '@mui/material';
import type { Theme } from '@mui/material/styles';
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { useSnackbar } from 'notistack';
import { helpLogToggleWindow } from 'src/requests/logging';
import { Console } from 'src/utils/console';
// see https://github.com/tauri-apps/tauri-plugin-log/blob/dev/webview-src/index.ts#L4
enum LogLevel {
@@ -40,7 +48,7 @@ const getLogLevelName = (value: LogLevel) => {
}
};
const getLogLevelColor = (level: LogLevel, theme: any) => {
const getLogLevelColor = (level: LogLevel, theme: Theme) => {
switch (level) {
case LogLevel.Trace:
return {
@@ -90,6 +98,7 @@ interface RecordPayload {
export const LogViewer: FC = () => {
const theme = useTheme();
const { enqueueSnackbar } = useSnackbar();
const unlisten = useRef<UnlistenFn | null>(null);
const [messages, setMessages] = useState<RecordPayload[]>([]);
const [messageCount, setMessageCount] = useState(0);
@@ -131,6 +140,44 @@ export const LogViewer: FC = () => {
};
}, []);
const handleCloseLogs = async () => {
try {
await helpLogToggleWindow();
} catch (e) {
Console.error(e);
enqueueSnackbar('Could not close the log window', { variant: 'error' });
}
};
const formatLine = (m: RecordPayload) => `${getLogLevelName(m.level)}\t${m.message}`;
const handleCopyAll = async () => {
if (messages.length === 0) return;
try {
const chronological = [...messages].reverse();
await writeText(chronological.map(formatLine).join('\n'));
} catch (e) {
Console.error(e);
enqueueSnackbar('Could not copy logs to the clipboard', { variant: 'error' });
}
};
const handleCopyLatest = async () => {
const latest = messages[0];
if (!latest) return;
try {
await writeText(formatLine(latest));
} catch (e) {
Console.error(e);
enqueueSnackbar('Could not copy to the clipboard', { variant: 'error' });
}
};
const handleClearList = () => {
setMessages([]);
setMessageCount(0);
};
return (
<Box
sx={{
@@ -180,54 +227,70 @@ export const LogViewer: FC = () => {
</TableRow>
</TableHead>
<TableBody>
{messages.map((m, index) => {
const levelColors = getLogLevelColor(m.level, theme);
return (
<TableRow
key={`log-${m.timestamp || index}`}
sx={{
bgcolor: levelColors.bg,
'&:hover': {
filter: 'brightness(0.95)',
},
}}
>
<TableCell
{messages.length === 0 ? (
<TableRow>
<TableCell colSpan={2} sx={{ border: 'none', py: 4, bgcolor: '#fafafa' }}>
<Typography
variant="body2"
color="text.secondary"
sx={{ textAlign: 'center', maxWidth: 560, mx: 'auto' }}
>
No log lines yet. The wallet streams new lines here in real time - older log history is not
replayed. Use the wallet or set RUST_LOG for more detail. Close with the button below or use Open
logs again in Settings - Advanced (same action toggles this window).
</Typography>
</TableCell>
</TableRow>
) : (
messages.map((m, index) => {
const levelColors = getLogLevelColor(m.level, theme);
return (
<TableRow
key={`log-${m.timestamp || index}`}
sx={{
padding: 1,
borderBottom: `1px solid ${theme.palette.divider}`,
width: '120px',
bgcolor: 'transparent',
bgcolor: levelColors.bg,
'&:hover': {
filter: 'brightness(0.95)',
},
}}
>
<Chip
label={getLogLevelName(m.level)}
variant="filled"
size="small"
<TableCell
sx={{
bgcolor: levelColors.chipBg,
color: levelColors.color,
fontWeight: 'medium',
minWidth: '70px',
border: '1px solid rgba(0,0,0,0.1)',
padding: 1,
borderBottom: `1px solid ${theme.palette.divider}`,
width: '120px',
bgcolor: 'transparent',
}}
/>
</TableCell>
<TableCell
sx={{
padding: 1,
fontFamily: 'monospace',
fontSize: '0.875rem',
borderBottom: `1px solid ${theme.palette.divider}`,
color: levelColors.color,
bgcolor: 'transparent',
}}
>
{m.message}
</TableCell>
</TableRow>
);
})}
>
<Chip
label={getLogLevelName(m.level)}
variant="filled"
size="small"
sx={{
bgcolor: levelColors.chipBg,
color: levelColors.color,
fontWeight: 'medium',
minWidth: '70px',
border: '1px solid rgba(0,0,0,0.1)',
}}
/>
</TableCell>
<TableCell
sx={{
padding: 1,
fontFamily: 'monospace',
fontSize: '0.875rem',
borderBottom: `1px solid ${theme.palette.divider}`,
color: levelColors.color,
bgcolor: 'transparent',
}}
>
{m.message}
</TableCell>
</TableRow>
);
})
)}
</TableBody>
</Table>
</TableContainer>
@@ -235,14 +298,32 @@ export const LogViewer: FC = () => {
<Box
sx={{
p: 1,
textAlign: 'right',
fontSize: '0.75rem',
borderTop: '1px solid #e0e0e0',
bgcolor: '#ffffff',
color: '#666666',
}}
>
{messageCount} log entries since opening this window
<Stack spacing={1}>
<Stack direction="row" alignItems="center" justifyContent="space-between" spacing={2} flexWrap="wrap">
<Typography variant="caption" sx={{ fontSize: '0.75rem' }}>
{messageCount} log entries since opening this window
</Typography>
<Button variant="outlined" size="small" onClick={handleCloseLogs}>
Close log window
</Button>
</Stack>
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap>
<Button variant="text" size="small" onClick={handleCopyLatest} disabled={messages.length === 0}>
Copy latest
</Button>
<Button variant="text" size="small" onClick={handleCopyAll} disabled={messages.length === 0}>
Copy all
</Button>
<Button variant="text" size="small" onClick={handleClearList} disabled={messages.length === 0}>
Clear list
</Button>
</Stack>
</Stack>
</Box>
</Box>
);
+44
View File
@@ -0,0 +1,44 @@
import type { DelegationWithEverything, WrappedDelegationEvent } from '@nymproject/types';
import { getAllPendingDelegations, getDelegationSummary } from 'src/requests';
import { decCoinToDisplay } from 'src/utils';
export type DelegationSummaryBundle = {
delegations: (DelegationWithEverything | WrappedDelegationEvent)[];
pendingDelegations: WrappedDelegationEvent[];
totalDelegations: string;
totalRewards: string;
totalDelegationsAndRewards: string;
};
export async function fetchDelegationSummaryQuery(): Promise<DelegationSummaryBundle> {
const data = await getDelegationSummary();
const pending = await getAllPendingDelegations();
const pendingOnNewNodes = pending.filter((event) => {
const some = data.delegations.some(({ node_identity }) => node_identity === event.node_identity);
return !some;
});
const items = data.delegations.map((delegation) => ({
...delegation,
amount: decCoinToDisplay(delegation.amount),
unclaimed_rewards: delegation.unclaimed_rewards && decCoinToDisplay(delegation.unclaimed_rewards),
cost_params: delegation.cost_params && {
...delegation.cost_params,
interval_operating_cost: decCoinToDisplay(delegation.cost_params.interval_operating_cost),
},
}));
const td = parseFloat(data.total_delegations.amount);
const tr = parseFloat(data.total_rewards.amount);
const delegationsAndRewards = Number.isFinite(td) && Number.isFinite(tr) ? (td + tr).toFixed(6) : '0';
return {
delegations: [...items, ...pendingOnNewNodes],
pendingDelegations: pending,
totalDelegations: `${data.total_delegations.amount} ${data.total_delegations.denom}`,
totalRewards: `${data.total_rewards.amount} ${data.total_rewards.denom}`,
totalDelegationsAndRewards: `${delegationsAndRewards} ${data.total_delegations.denom}`,
};
}
export { delegationQueryKeys } from './delegationQueryKeys';
@@ -0,0 +1,7 @@
import { delegationQueryKeys } from './delegationQueryKeys';
describe('delegationQueryKeys', () => {
it('builds a stable summary key per client address', () => {
expect(delegationQueryKeys.summary('nyc1test')).toStrictEqual(['delegation', 'summary', 'nyc1test']);
});
});
@@ -0,0 +1,4 @@
export const delegationQueryKeys = {
all: ['delegation'] as const,
summary: (clientAddress: string) => [...delegationQueryKeys.all, 'summary', clientAddress] as const,
};
+77 -55
View File
@@ -1,5 +1,7 @@
import React, { createContext, FC, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { getDelegationSummary, undelegateFromMixnode } from 'src/requests/delegation';
import React, { createContext, FC, useCallback, useContext, useEffect, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { undelegateFromMixnode } from 'src/requests/delegation';
import {
DecCoin,
DelegationWithEverything,
@@ -9,19 +11,19 @@ import {
WrappedDelegationEvent,
} from '@nymproject/types';
import type { Network } from 'src/types';
import {
delegateToMixnode,
getAllPendingDelegations,
vestingDelegateToMixnode,
vestingUndelegateFromMixnode,
} from 'src/requests';
import { delegateToMixnode, vestingDelegateToMixnode, vestingUndelegateFromMixnode } from 'src/requests';
import { TPoolOption } from 'src/components';
import { decCoinToDisplay } from 'src/utils';
import { Console } from 'src/utils/console';
import { AppContext } from 'src/context/main';
import { delegationQueryKeys } from './delegationQueryKeys';
import { fetchDelegationSummaryQuery } from './delegationQuery';
export type TDelegationContext = {
delegationItemErrors?: { nodeId: string; errors: string };
isLoading: boolean;
isFetching: boolean;
isError: boolean;
lastUpdatedAtMs: number;
delegations?: TDelegations;
pendingDelegations?: WrappedDelegationEvent[];
totalDelegations?: string;
@@ -52,7 +54,10 @@ export const isDelegation = (delegation: DelegationWithEvent): delegation is Del
'owner' in delegation;
export const DelegationContext = createContext<TDelegationContext>({
isLoading: true,
isLoading: false,
isFetching: false,
isError: false,
lastUpdatedAtMs: 0,
refresh: async () => undefined,
addDelegation: async () => {
throw new Error('Not implemented');
@@ -66,18 +71,47 @@ export const DelegationContext = createContext<TDelegationContext>({
setDelegationItemErrors: () => undefined,
});
function isDelegationRoutePath(pathname: string): boolean {
return pathname === '/delegation' || pathname.endsWith('/delegation');
}
export const DelegationContextProvider: FC<{
network?: Network;
children: React.ReactNode;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}> = ({ network, children }) => {
const [isLoading, setIsLoading] = useState(true);
const [delegationItemErrors, setDelegationItemErrors] = useState<{ nodeId: string; errors: string }>();
const [delegations, setDelegations] = useState<undefined | TDelegations>();
const [totalDelegations, setTotalDelegations] = useState<undefined | string>();
const [totalRewards, setTotalRewards] = useState<undefined | string>();
const [totalDelegationsAndRewards, setTotalDelegationsAndRewards] = useState<undefined | string>();
const [pendingDelegations, setPendingDelegations] = useState<WrappedDelegationEvent[]>();
const location = useLocation();
const queryClient = useQueryClient();
const { clientDetails } = useContext(AppContext);
const clientAddress = clientDetails?.client_address;
const onDelegationRoute = isDelegationRoutePath(location.pathname);
const [delegationItemErrors, setDelegationItemErrors] = React.useState<{ nodeId: string; errors: string }>();
const query = useQuery({
queryKey: delegationQueryKeys.summary(clientAddress ?? ''),
queryFn: fetchDelegationSummaryQuery,
enabled: Boolean(clientAddress) && onDelegationRoute,
staleTime: 5 * 60 * 1000,
gcTime: 30 * 60 * 1000,
});
useEffect(() => {
if (!clientAddress) {
queryClient.removeQueries({ queryKey: delegationQueryKeys.all });
}
}, [clientAddress, queryClient]);
const bundle = clientAddress && onDelegationRoute ? query.data : undefined;
const refresh = useCallback(async () => {
if (!clientAddress || !onDelegationRoute) {
return;
}
await queryClient.invalidateQueries({
queryKey: delegationQueryKeys.summary(clientAddress),
});
}, [clientAddress, onDelegationRoute, queryClient]);
const addDelegation = async (data: { mix_id: number; amount: DecCoin }, tokenPool: TPoolOption, fee?: FeeDetails) => {
try {
@@ -91,51 +125,29 @@ export const DelegationContextProvider: FC<{
return tx;
} catch (e) {
throw new Error(e as string);
const message = e instanceof Error ? e.message : String(e);
throw new Error(message);
}
};
const refresh = useCallback(async () => {
setIsLoading(true);
try {
const data = await getDelegationSummary();
const pending = await getAllPendingDelegations();
const delegations = bundle?.delegations;
const pendingDelegations = bundle?.pendingDelegations;
const totalDelegations = bundle?.totalDelegations;
const totalRewards = bundle?.totalRewards;
const totalDelegationsAndRewards = bundle?.totalDelegationsAndRewards;
const pendingOnNewNodes = pending.filter((event) => {
const some = data.delegations.some(({ node_identity }) => node_identity === event.node_identity);
return !some;
});
const items = data.delegations.map((delegation) => ({
...delegation,
amount: decCoinToDisplay(delegation.amount),
unclaimed_rewards: delegation.unclaimed_rewards && decCoinToDisplay(delegation.unclaimed_rewards),
cost_params: delegation.cost_params && {
...delegation.cost_params,
interval_operating_cost: decCoinToDisplay(delegation.cost_params.interval_operating_cost),
},
}));
const delegationsAndRewards = (+data.total_delegations.amount + +data.total_rewards.amount).toFixed(6);
setPendingDelegations(pending);
setDelegations([...items, ...pendingOnNewNodes]);
setTotalDelegations(`${data.total_delegations.amount} ${data.total_delegations.denom}`);
setTotalRewards(`${data.total_rewards.amount} ${data.total_rewards.denom}`);
setTotalDelegationsAndRewards(`${delegationsAndRewards} ${data.total_delegations.denom}`);
} catch (e) {
Console.error(e);
}
setIsLoading(false);
}, []);
useEffect(() => {
refresh();
}, []);
const isLoading = Boolean(clientAddress) && onDelegationRoute && query.isPending;
const isFetching = Boolean(clientAddress) && onDelegationRoute && query.isFetching;
const isError = Boolean(clientAddress) && onDelegationRoute && query.isError && !query.data;
const lastUpdatedAtMs = bundle ? query.dataUpdatedAt : 0;
const memoizedValue = useMemo(
() => ({
delegationItemErrors,
isLoading,
isFetching,
isError,
lastUpdatedAtMs,
delegations,
pendingDelegations,
totalDelegations,
@@ -148,16 +160,26 @@ export const DelegationContextProvider: FC<{
undelegateVesting: vestingUndelegateFromMixnode,
}),
[
isLoading,
delegations,
delegationItemErrors,
isLoading,
isFetching,
isError,
lastUpdatedAtMs,
delegations,
pendingDelegations,
totalDelegations,
totalRewards,
totalDelegationsAndRewards,
refresh,
],
);
useEffect(() => {
if (query.isError && query.error) {
Console.error(query.error);
}
}, [query.isError, query.error]);
return <DelegationContext.Provider value={memoizedValue}>{children}</DelegationContext.Provider>;
};
+37 -56
View File
@@ -7,7 +7,7 @@ import {
FeeDetails,
TransactionExecuteResult,
} from '@nymproject/types';
import { DelegationContext, TDelegationTransaction } from '../delegations';
import { DelegationContext } from '../delegations';
import { mockSleep } from './utils';
import { TPoolOption } from '../../components';
@@ -71,6 +71,9 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
const [error, setError] = useState<string>();
const [delegations, setDelegations] = useState<undefined | DelegationWithEverything[]>();
const [totalDelegations, setTotalDelegations] = useState<undefined | string>();
const [totalRewards, setTotalRewards] = useState<undefined | string>();
const [totalDelegationsAndRewards, setTotalDelegationsAndRewards] = useState<undefined | string>();
const [lastUpdatedAtMs, setLastUpdatedAtMs] = useState(0);
const [delegationItemErrors, setDelegationItemErrors] = useState<{ nodeId: string; errors: string }>();
const triggerStateUpdate = () => setTrigger(new Date());
@@ -81,8 +84,16 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
const recalculate = async () => {
const newDelegations = await getDelegations();
const newTotalDelegations = `${newDelegations.length * 100} NYM`;
const rewardsSum = newDelegations.reduce((acc, d) => {
const n = parseFloat(d.unclaimed_rewards?.amount ?? '0');
return acc + (Number.isFinite(n) ? n : 0);
}, 0);
const newTotalRewards = `${rewardsSum} nym`;
setDelegations(newDelegations);
setTotalDelegations(newTotalDelegations);
setTotalRewards(newTotalRewards);
setTotalDelegationsAndRewards(`${newTotalDelegations} + ${newTotalRewards}`);
setLastUpdatedAtMs(Date.now());
};
const addDelegation = async (
@@ -91,7 +102,6 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
_fee?: FeeDetails,
): Promise<TransactionExecuteResult> => {
await mockSleep(SLEEP_MS);
// mockDelegations.push({ ...newDelegation });
await recalculate();
triggerStateUpdate();
@@ -118,52 +128,6 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
};
};
const updateDelegation = async (
newDelegation: DelegationWithEverything,
ignorePendingForStorybook?: boolean,
): Promise<TDelegationTransaction> => {
if (ignorePendingForStorybook) {
mockDelegations = mockDelegations.map((d) => {
if (d.node_identity === newDelegation.node_identity) {
return { ...newDelegation };
}
return d;
});
await recalculate();
triggerStateUpdate();
return {
transactionUrl:
'https://sandbox-blocks.nymtech.net/transactions/55303CD4B91FAC4C2715E40EBB52BB3B92829D9431B3A279D37B5CC58432E354',
};
}
await mockSleep(SLEEP_MS);
mockDelegations = mockDelegations.map((d) => {
if (d.node_identity === newDelegation.node_identity) {
return { ...newDelegation, isPending: { blockHeight: 1234, actionType: 'delegate' } };
}
return d;
});
await recalculate();
triggerStateUpdate();
setTimeout(async () => {
mockDelegations = mockDelegations.map((d) => {
if (d.node_identity === newDelegation.node_identity) {
return { ...d, isPending: undefined };
}
return d;
});
await recalculate();
triggerStateUpdate();
}, 3000);
return {
transactionUrl:
'https://sandbox-blocks.nymtech.net/transactions/55303CD4B91FAC4C2715E40EBB52BB3B92829D9431B3A279D37B5CC58432E354',
};
};
const undelegate = async (mix_id: number, _fee?: Fee): Promise<TransactionExecuteResult> => {
await mockSleep(SLEEP_MS);
mockDelegations = mockDelegations.map((d) => {
@@ -193,10 +157,8 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
};
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const undelegateVesting = async (mix_id: number, _fee?: FeeDetails) => ({
const undelegateVesting = async (_mix_id: number): Promise<TransactionExecuteResult> => ({
msg_responses_json: '',
data_json: '',
logs_json: '',
transaction_hash: '',
gas_info: {
@@ -210,6 +172,9 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
setIsLoading(true);
setError(undefined);
setTotalDelegations(undefined);
setTotalRewards(undefined);
setTotalDelegationsAndRewards(undefined);
setLastUpdatedAtMs(0);
setDelegations([]);
};
@@ -227,7 +192,6 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
}, []);
useEffect(() => {
// reset state and refresh
resetState();
refresh();
}, []);
@@ -237,17 +201,34 @@ export const MockDelegationContextProvider: FCWithChildren = ({ children }) => {
delegationItemErrors,
setDelegationItemErrors,
isLoading,
error,
isFetching: isLoading,
isError: Boolean(error),
lastUpdatedAtMs,
delegations,
pendingDelegations: [],
totalDelegations,
totalRewards,
totalDelegationsAndRewards,
refresh,
getDelegations,
addDelegation,
updateDelegation,
undelegate,
undelegateVesting,
}),
[isLoading, error, delegations, totalDelegations, trigger],
[
isLoading,
error,
delegations,
totalDelegations,
totalRewards,
totalDelegationsAndRewards,
lastUpdatedAtMs,
refresh,
addDelegation,
undelegate,
undelegateVesting,
delegationItemErrors,
trigger,
],
);
return <DelegationContext.Provider value={memoizedValue}>{children}</DelegationContext.Provider>;
+11 -7
View File
@@ -3,25 +3,29 @@ import { FeeDetails, TransactionExecuteResult } from '@nymproject/types';
import { useDelegationContext } from './delegations';
import { claimDelegatorRewards } from '../requests';
export type TRewardsTransaction = {
transactionUrl: string;
transactionHash: string;
};
type TRewardsContext = {
isLoading: boolean;
error?: string;
totalRewards?: string;
refresh: () => Promise<void>;
claimRewards: (mixId: number, fee?: FeeDetails) => Promise<TransactionExecuteResult[]>;
};
export type TRewardsTransaction = {
transactionUrl: string;
transactionHash: string;
redeemAllRewards: () => Promise<TRewardsTransaction[]>;
};
export const RewardsContext = createContext<TRewardsContext>({
isLoading: true,
isLoading: false,
refresh: async () => undefined,
claimRewards: async () => {
throw new Error('Not implemented');
},
redeemAllRewards: async () => {
throw new Error('Not implemented');
},
});
export const RewardsContextProvider: FCWithChildren = ({ children }) => {
@@ -47,7 +51,7 @@ export const RewardsContextProvider: FCWithChildren = ({ children }) => {
throw new Error('Not implemented');
},
}),
[isLoading, error, totalRewards],
[isLoading, error, totalRewards, refresh],
);
return <RewardsContext.Provider value={memoizedValue}>{children}</RewardsContext.Provider>;
+6 -3
View File
@@ -1,15 +1,18 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { ErrorBoundary } from 'react-error-boundary';
import { SnackbarProvider } from 'notistack';
import { LogViewer } from './components/LogViewer';
import { ErrorFallback } from './components';
import { NymWalletTheme } from './theme';
const Log = () => (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<NymWalletTheme>
<LogViewer />
</NymWalletTheme>
<SnackbarProvider anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}>
<NymWalletTheme>
<LogViewer />
</NymWalletTheme>
</SnackbarProvider>
</ErrorBoundary>
);
+47 -15
View File
@@ -1,4 +1,4 @@
import React, { FC, useContext, useEffect, useState } from 'react';
import React, { FC, useCallback, useContext, useEffect, useRef, useState } from 'react';
import { OpenInNew } from '@mui/icons-material';
import { Alert, AlertTitle, Box, Button, CircularProgress, LinearProgress, Stack, Typography } from '@mui/material';
import { alpha, useTheme } from '@mui/material/styles';
@@ -11,6 +11,7 @@ import { Console } from 'src/utils/console';
import { OverSaturatedBlockerModal } from 'src/components/Delegation/DelegateBlocker';
import { getSpendableCoins, migrateVestedDelegations, userBalance } from 'src/requests';
import { LoadingModal } from 'src/components/Modals/LoadingModal';
import { format } from 'date-fns';
import { getIntervalAsDate, toPercentIntegerString } from 'src/utils';
import { DelegationContextProvider, isDelegation, TDelegations, useDelegationContext } from '../../context/delegations';
import { RewardsContextProvider, useRewardsContext } from '../../context/rewards';
@@ -44,6 +45,9 @@ export const Delegation: FC = () => {
const {
delegations,
isLoading,
isFetching,
isError,
lastUpdatedAtMs,
addDelegation,
undelegate,
undelegateVesting,
@@ -55,9 +59,11 @@ export const Delegation: FC = () => {
[delegations],
);
const { refresh: refreshRewards, claimRewards } = useRewardsContext();
const { claimRewards } = useRewardsContext();
const refresh = async () => Promise.all([refreshDelegations(), refreshRewards()]);
const refreshWithData = useCallback(async () => {
await refreshDelegations();
}, [refreshDelegations]);
// If an action modal is open, don't show the loading modal
const isActionModalOpen =
@@ -83,36 +89,54 @@ export const Delegation: FC = () => {
};
};
const getNextInterval = async () => {
const getNextInterval = useCallback(async () => {
try {
const { nextEpoch: newNextEpoch } = await getIntervalAsDate();
setNextEpoch(newNextEpoch);
} catch {
setNextEpoch(Error());
}
};
}, []);
const refreshWithIntervalUpdate = async () => {
refresh();
getNextInterval();
};
const refreshWithIntervalUpdate = useCallback(async () => {
await refreshWithData();
await getNextInterval();
}, [refreshWithData, getNextInterval]);
const refreshWithIntervalUpdateRef = useRef(refreshWithIntervalUpdate);
refreshWithIntervalUpdateRef.current = refreshWithIntervalUpdate;
// Refresh the rewards and delegations periodically when page is mounted
useEffect(() => {
const timer = setInterval(refreshWithIntervalUpdate, 5 * 60 * 1000); // every 5 minutes
const timer = setInterval(() => {
refreshWithIntervalUpdateRef.current().catch((err) => {
Console.error(err);
});
}, 5 * 60 * 1000);
return () => clearInterval(timer);
}, []);
const doMigrateNow = async () => {
setShowVestingMigrationProgressModal(true);
await migrateVestedDelegations();
await refresh();
await refreshDelegations();
setShowVestingMigrationProgressModal(false);
};
useEffect(() => {
refreshWithIntervalUpdate();
}, [clientDetails, confirmationModalProps]);
getNextInterval().catch((err) => {
Console.error(err);
});
}, [clientDetails, getNextInterval]);
const prevConfirmationModalProps = useRef<DelegationModalProps | undefined>(undefined);
useEffect(() => {
if (prevConfirmationModalProps.current !== undefined && confirmationModalProps === undefined) {
refreshWithIntervalUpdate().catch((err) => {
Console.error(err);
});
}
prevConfirmationModalProps.current = confirmationModalProps;
}, [confirmationModalProps, refreshWithIntervalUpdate]);
const handleDelegationItemActionClick = (item: DelegationWithEverything, action: DelegationListItemActions) => {
if (
@@ -409,6 +433,14 @@ export const Delegation: FC = () => {
}}
>
<Stack spacing={3}>
{isError && (
<Alert severity="error">Could not load delegation data. Check your connection and try again.</Alert>
)}
{lastUpdatedAtMs > 0 && (
<Typography variant="caption" color="text.secondary">
Last updated: {format(lastUpdatedAtMs, 'yyyy-MM-dd HH:mm:ss')}
</Typography>
)}
{!!delegations?.length && (
<Stack
direction={{ xs: 'column', sm: 'row' }}
@@ -471,7 +503,7 @@ export const Delegation: FC = () => {
</Stack>
)}
{isLoading && delegations !== undefined && !isActionModalOpen && (
{isFetching && delegations !== undefined && !isActionModalOpen && (
<LinearProgress
sx={{
height: 3,
+349 -13
View File
@@ -1463,7 +1463,7 @@
"@cosmjs/math" "^0.32.4"
"@cosmjs/utils" "^0.32.4"
"@cosmjs/cosmwasm-stargate@^0.32.3", "@cosmjs/cosmwasm-stargate@^0.32.4":
"@cosmjs/cosmwasm-stargate@^0.32.3":
version "0.32.4"
resolved "https://registry.yarnpkg.com/@cosmjs/cosmwasm-stargate/-/cosmwasm-stargate-0.32.4.tgz#2ee93f2cc0b1c146ac369b2bf8ef9ee2e159fd50"
integrity sha512-Fuo9BGEiB+POJ5WeRyBGuhyKR1ordvxZGLPuPosFJOH9U0gKMgcjwKMCgAlWFkMlHaTB+tNdA8AifWiHrI7VgA==
@@ -1851,18 +1851,41 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6"
integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==
"@eslint-community/eslint-utils@^4.2.0":
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1":
version "4.9.1"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595"
integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==
dependencies:
eslint-visitor-keys "^3.4.3"
"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1":
"@eslint-community/regexpp@^4.12.1", "@eslint-community/regexpp@^4.12.2", "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1":
version "4.12.2"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b"
integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==
"@eslint/config-array@^0.21.2":
version "0.21.2"
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.2.tgz#f29e22057ad5316cf23836cee9a34c81fffcb7e6"
integrity sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==
dependencies:
"@eslint/object-schema" "^2.1.7"
debug "^4.3.1"
minimatch "^3.1.5"
"@eslint/config-helpers@^0.4.2":
version "0.4.2"
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda"
integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==
dependencies:
"@eslint/core" "^0.17.0"
"@eslint/core@^0.17.0":
version "0.17.0"
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c"
integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==
dependencies:
"@types/json-schema" "^7.0.15"
"@eslint/eslintrc@^2.1.4":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad"
@@ -1878,11 +1901,44 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/eslintrc@^3.3.5":
version "3.3.5"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.5.tgz#c131793cfc1a7b96f24a83e0a8bbd4b881558c60"
integrity sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==
dependencies:
ajv "^6.14.0"
debug "^4.3.2"
espree "^10.0.1"
globals "^14.0.0"
ignore "^5.2.0"
import-fresh "^3.2.1"
js-yaml "^4.1.1"
minimatch "^3.1.5"
strip-json-comments "^3.1.1"
"@eslint/js@8.57.1":
version "8.57.1"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
"@eslint/js@9.39.4":
version "9.39.4"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.4.tgz#a3f83bfc6fd9bf33a853dfacd0b49b398eb596c1"
integrity sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==
"@eslint/object-schema@^2.1.7":
version "2.1.7"
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad"
integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==
"@eslint/plugin-kit@^0.4.1":
version "0.4.1"
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2"
integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==
dependencies:
"@eslint/core" "^0.17.0"
levn "^0.4.1"
"@ethersproject/address@^5.6.0":
version "5.8.0"
resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.8.0.tgz#3007a2c352eee566ad745dca1dbbebdb50a6a983"
@@ -2035,6 +2091,27 @@
resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.9.11.tgz#9ce96e7746625a89239f68ca57c4f654264c17ef"
integrity sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ==
"@humanfs/core@^0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.2.tgz#a8272ca03b2acf492670222b2320b6c421bfde60"
integrity sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==
dependencies:
"@humanfs/types" "^0.15.0"
"@humanfs/node@^0.16.6":
version "0.16.8"
resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.8.tgz#8f800cccc13f4f8cd3116e2d9c0a94939da3e3ed"
integrity sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==
dependencies:
"@humanfs/core" "^0.19.2"
"@humanfs/types" "^0.15.0"
"@humanwhocodes/retry" "^0.4.0"
"@humanfs/types@^0.15.0":
version "0.15.0"
resolved "https://registry.yarnpkg.com/@humanfs/types/-/types-0.15.0.tgz#f2a09f62012390b2bff3fc6fb248ddec8c09a090"
integrity sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==
"@humanwhocodes/config-array@^0.13.0":
version "0.13.0"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748"
@@ -2054,6 +2131,11 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba"
integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==
"@hutson/parse-repository-url@^3.0.0":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340"
@@ -6209,6 +6291,11 @@
dependencies:
remove-accents "0.5.0"
"@tanstack/query-core@5.100.9":
version "5.100.9"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.100.9.tgz#dcf44ef25cf42a4da229bcab1d8d33e80a740a99"
integrity sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ==
"@tanstack/query-core@5.90.5":
version "5.90.5"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.90.5.tgz#0175f9f517514906db8ab379589ed3f96694ecc4"
@@ -6231,6 +6318,13 @@
resolved "https://registry.yarnpkg.com/@tanstack/react-query-next-experimental/-/react-query-next-experimental-5.90.2.tgz#4454119d2e5ac7aa42dfb540ba300e956f4336df"
integrity sha512-f7vJ9SHRO5GjiTBIpaSdLTczPlDybu6OzLG2thcNev55Qi69JkYsSOyUVWKpa3tGgufyJx1kDAO5lJjv4jF/bw==
"@tanstack/react-query@^5.62.0":
version "5.100.9"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.100.9.tgz#0c701bf56f38b484602255a92d4c9e452a04807d"
integrity sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A==
dependencies:
"@tanstack/query-core" "5.100.9"
"@tanstack/react-query@^5.64.2":
version "5.90.5"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.90.5.tgz#545e61282c787bd87ac5785da9a4943462f78ef6"
@@ -6286,7 +6380,7 @@
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz#1dff176df9cc8f93c78c5e46bcea11079b397578"
integrity sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==
"@tauri-apps/api@2.10.1", "@tauri-apps/api@^2.10.1", "@tauri-apps/api@^2.8.0":
"@tauri-apps/api@^2.10.1", "@tauri-apps/api@^2.8.0":
version "2.10.1"
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.10.1.tgz#57c1bae6114ec33d977eb2b50dfefc25fa84fc93"
integrity sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==
@@ -6682,7 +6776,7 @@
dependencies:
"@types/estree" "*"
"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.8":
"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.6", "@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==
@@ -7186,7 +7280,7 @@
resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.5.tgz#8ce8623ed7a36e3a76d1c0b539708dfb2e859bc0"
integrity sha512-FZJgC5Bxuqg7Rhsm/bx6gAruHHhDQ55r+s0JhDh8CQ16fD7NsJJ+p8YMMQDhSQoIrSmjpqqYWA96oQVMNkjRyA==
"@typescript-eslint/eslint-plugin@5.62.0", "@typescript-eslint/eslint-plugin@^5.13.0", "@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/eslint-plugin@^8.56.1":
"@typescript-eslint/eslint-plugin@^5.13.0", "@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db"
integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==
@@ -7202,6 +7296,20 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/eslint-plugin@^8.56.1":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.2.tgz#f37b2c189a0177141fe3de3b08f2a83991bfdbfa"
integrity sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==
dependencies:
"@eslint-community/regexpp" "^4.12.2"
"@typescript-eslint/scope-manager" "8.59.2"
"@typescript-eslint/type-utils" "8.59.2"
"@typescript-eslint/utils" "8.59.2"
"@typescript-eslint/visitor-keys" "8.59.2"
ignore "^7.0.5"
natural-compare "^1.4.0"
ts-api-utils "^2.5.0"
"@typescript-eslint/experimental-utils@^5.3.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz#14559bf73383a308026b427a4a6129bae2146741"
@@ -7209,7 +7317,7 @@
dependencies:
"@typescript-eslint/utils" "5.62.0"
"@typescript-eslint/parser@5.62.0", "@typescript-eslint/parser@^5.13.0", "@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser@^8.56.1":
"@typescript-eslint/parser@^5.13.0", "@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7"
integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==
@@ -7219,6 +7327,26 @@
"@typescript-eslint/typescript-estree" "5.62.0"
debug "^4.3.4"
"@typescript-eslint/parser@^8.56.1":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.59.2.tgz#e2fd0084baa5dd0c24cd789af1c72cbc3a7a1c62"
integrity sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==
dependencies:
"@typescript-eslint/scope-manager" "8.59.2"
"@typescript-eslint/types" "8.59.2"
"@typescript-eslint/typescript-estree" "8.59.2"
"@typescript-eslint/visitor-keys" "8.59.2"
debug "^4.4.3"
"@typescript-eslint/project-service@8.59.2":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.59.2.tgz#f8b8cbf8692e3a51c2c394acf8cf6900f7e755af"
integrity sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==
dependencies:
"@typescript-eslint/tsconfig-utils" "^8.59.2"
"@typescript-eslint/types" "^8.59.2"
debug "^4.4.3"
"@typescript-eslint/scope-manager@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c"
@@ -7227,6 +7355,19 @@
"@typescript-eslint/types" "5.62.0"
"@typescript-eslint/visitor-keys" "5.62.0"
"@typescript-eslint/scope-manager@8.59.2":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.2.tgz#63cbd0af2e3180949d6be81122cc555bc71e736d"
integrity sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==
dependencies:
"@typescript-eslint/types" "8.59.2"
"@typescript-eslint/visitor-keys" "8.59.2"
"@typescript-eslint/tsconfig-utils@8.59.2", "@typescript-eslint/tsconfig-utils@^8.59.2":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.2.tgz#6e92bc412083753185a79c9f1431e78169d9232f"
integrity sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==
"@typescript-eslint/type-utils@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a"
@@ -7237,11 +7378,27 @@
debug "^4.3.4"
tsutils "^3.21.0"
"@typescript-eslint/type-utils@8.59.2":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.59.2.tgz#a60a1192a804fa472a92c41656853ac6a9ba7176"
integrity sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==
dependencies:
"@typescript-eslint/types" "8.59.2"
"@typescript-eslint/typescript-estree" "8.59.2"
"@typescript-eslint/utils" "8.59.2"
debug "^4.4.3"
ts-api-utils "^2.5.0"
"@typescript-eslint/types@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
"@typescript-eslint/types@8.59.2", "@typescript-eslint/types@^8.59.2":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.59.2.tgz#01caabcd7e4715c33ad5e11cab260829714d6b9c"
integrity sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==
"@typescript-eslint/typescript-estree@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b"
@@ -7255,6 +7412,21 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@8.59.2":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.2.tgz#6a217ef65b18dbd12c718fc86a675d1d7a1414cc"
integrity sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==
dependencies:
"@typescript-eslint/project-service" "8.59.2"
"@typescript-eslint/tsconfig-utils" "8.59.2"
"@typescript-eslint/types" "8.59.2"
"@typescript-eslint/visitor-keys" "8.59.2"
debug "^4.4.3"
minimatch "^10.2.2"
semver "^7.7.3"
tinyglobby "^0.2.15"
ts-api-utils "^2.5.0"
"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.10.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
@@ -7269,6 +7441,16 @@
eslint-scope "^5.1.1"
semver "^7.3.7"
"@typescript-eslint/utils@8.59.2":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.59.2.tgz#ff619a6a3075f4017fa91b8610b752a8ca3366aa"
integrity sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==
dependencies:
"@eslint-community/eslint-utils" "^4.9.1"
"@typescript-eslint/scope-manager" "8.59.2"
"@typescript-eslint/types" "8.59.2"
"@typescript-eslint/typescript-estree" "8.59.2"
"@typescript-eslint/visitor-keys@5.62.0":
version "5.62.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
@@ -7277,6 +7459,14 @@
"@typescript-eslint/types" "5.62.0"
eslint-visitor-keys "^3.3.0"
"@typescript-eslint/visitor-keys@8.59.2":
version "8.59.2"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.2.tgz#5ccc486913cd347883d69158836b1189a660bfe6"
integrity sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==
dependencies:
"@typescript-eslint/types" "8.59.2"
eslint-visitor-keys "^5.0.0"
"@uidotdev/usehooks@^2.4.1":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@uidotdev/usehooks/-/usehooks-2.4.1.tgz#4b733eaeae09a7be143c6c9ca158b56cc1ea75bf"
@@ -7999,6 +8189,16 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^6.14.0:
version "6.15.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.15.0.tgz#07e982c74626167aa7a2495c53817892d7139492"
integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==
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"
ajv@^8.0.0, ajv@^8.9.0:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
@@ -8695,6 +8895,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
balanced-match@^4.0.2:
version "4.0.4"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a"
integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==
base-x@^3.0.2, base-x@^3.0.6:
version "3.0.11"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.11.tgz#40d80e2a1aeacba29792ccc6c5354806421287ff"
@@ -8917,6 +9122,13 @@ brace-expansion@^2.0.1:
dependencies:
balanced-match "^1.0.0"
brace-expansion@^5.0.5:
version "5.0.5"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb"
integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==
dependencies:
balanced-match "^4.0.2"
braces@^2.3.1, braces@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
@@ -10745,7 +10957,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
dependencies:
ms "2.0.0"
debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.4.0, debug@^4.4.1:
debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3:
version "4.4.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
@@ -11871,12 +12083,30 @@ eslint-scope@^7.2.2:
esrecurse "^4.3.0"
estraverse "^5.2.0"
eslint-scope@^8.4.0:
version "8.4.0"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82"
integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==
dependencies:
esrecurse "^4.3.0"
estraverse "^5.2.0"
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
eslint@8.57.1, eslint@^8.57.1, eslint@^9, eslint@^9.26.0:
eslint-visitor-keys@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1"
integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
eslint-visitor-keys@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be"
integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==
eslint@^8.57.1:
version "8.57.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9"
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
@@ -11920,6 +12150,55 @@ eslint@8.57.1, eslint@^8.57.1, eslint@^9, eslint@^9.26.0:
strip-ansi "^6.0.1"
text-table "^0.2.0"
eslint@^9, eslint@^9.26.0:
version "9.39.4"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.4.tgz#855da1b2e2ad66dc5991195f35e262bcec8117b5"
integrity sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==
dependencies:
"@eslint-community/eslint-utils" "^4.8.0"
"@eslint-community/regexpp" "^4.12.1"
"@eslint/config-array" "^0.21.2"
"@eslint/config-helpers" "^0.4.2"
"@eslint/core" "^0.17.0"
"@eslint/eslintrc" "^3.3.5"
"@eslint/js" "9.39.4"
"@eslint/plugin-kit" "^0.4.1"
"@humanfs/node" "^0.16.6"
"@humanwhocodes/module-importer" "^1.0.1"
"@humanwhocodes/retry" "^0.4.2"
"@types/estree" "^1.0.6"
ajv "^6.14.0"
chalk "^4.0.0"
cross-spawn "^7.0.6"
debug "^4.3.2"
escape-string-regexp "^4.0.0"
eslint-scope "^8.4.0"
eslint-visitor-keys "^4.2.1"
espree "^10.4.0"
esquery "^1.5.0"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
file-entry-cache "^8.0.0"
find-up "^5.0.0"
glob-parent "^6.0.2"
ignore "^5.2.0"
imurmurhash "^0.1.4"
is-glob "^4.0.0"
json-stable-stringify-without-jsonify "^1.0.1"
lodash.merge "^4.6.2"
minimatch "^3.1.5"
natural-compare "^1.4.0"
optionator "^0.9.3"
espree@^10.0.1, espree@^10.4.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837"
integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==
dependencies:
acorn "^8.15.0"
acorn-jsx "^5.3.2"
eslint-visitor-keys "^4.2.1"
espree@^9.6.0, espree@^9.6.1:
version "9.6.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
@@ -11934,7 +12213,7 @@ esprima@^4.0.0, esprima@^4.0.1:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
esquery@^1.4.2:
esquery@^1.4.2, esquery@^1.5.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d"
integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==
@@ -12330,6 +12609,13 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
file-entry-cache@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
dependencies:
flat-cache "^4.0.0"
file-loader@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
@@ -12474,6 +12760,14 @@ flat-cache@^3.0.4:
keyv "^4.5.3"
rimraf "^3.0.2"
flat-cache@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
dependencies:
flatted "^3.2.9"
keyv "^4.5.4"
flat@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
@@ -13067,6 +13361,11 @@ globals@^13.19.0:
dependencies:
type-fest "^0.20.2"
globals@^14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
globalthis@^1.0.0, globalthis@^1.0.1, globalthis@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236"
@@ -13781,6 +14080,11 @@ ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
ignore@^7.0.5:
version "7.0.5"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9"
integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==
immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
@@ -15594,6 +15898,13 @@ js-yaml@^3.10.0, js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b"
integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
dependencies:
argparse "^2.0.1"
jsdom@^16.6.0:
version "16.7.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"
@@ -15814,7 +16125,7 @@ junk@^3.1.0:
resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==
keyv@^4.5.3:
keyv@^4.5.3, keyv@^4.5.4:
version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
@@ -17070,6 +17381,13 @@ minimatch@^10.0.3:
dependencies:
"@isaacs/brace-expansion" "^5.0.0"
minimatch@^10.2.2:
version "10.2.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1"
integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==
dependencies:
brace-expansion "^5.0.5"
minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -17077,7 +17395,7 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"
minimatch@^3.0.5:
minimatch@^3.0.5, minimatch@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e"
integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==
@@ -18523,6 +18841,11 @@ picomatch@^4.0.3:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
picomatch@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589"
integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==
pidtree@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a"
@@ -20593,7 +20916,7 @@ semver@^6.0.0, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.7.2, semver@^7.7.4:
semver@^7.7.2, semver@^7.7.3, semver@^7.7.4:
version "7.7.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a"
integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==
@@ -21904,6 +22227,14 @@ tinyglobby@^0.2.13:
fdir "^6.5.0"
picomatch "^4.0.3"
tinyglobby@^0.2.15:
version "0.2.16"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6"
integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==
dependencies:
fdir "^6.5.0"
picomatch "^4.0.4"
tinyrainbow@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz#5c57d2fc0fb3d1afd78465c33ca885d04f02abb5"
@@ -22063,6 +22394,11 @@ trough@^2.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f"
integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==
ts-api-utils@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1"
integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==
ts-dedent@^2.0.0, ts-dedent@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"