diff --git a/.gitignore b/.gitignore index 2b7e9672a0f0c23298dbe55aa0e4058cd1b91d65..667cd7b8ef3bbafeed225a7e73efb4e844acc0d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ + +issue.md loginDetails.json package-lock.json @@ -78,3 +80,6 @@ Sessionx.vim tags # Persistent undo [._]*.un~ + +# Local Reacordings +Recordings/* diff --git a/app/App.tsx b/app/App.tsx index bdb35ee292871071511a8f32d0a8313923fc3721..653da7883f4bcf1b82810ef3b133a17941e3e925 100644 --- a/app/App.tsx +++ b/app/App.tsx @@ -1,10 +1,8 @@ import React from 'react'; import { createMuiTheme, ThemeProvider } from '@material-ui/core'; -import { HashRouter as Router, Switch, Route } from 'react-router-dom'; -import routes from './routes'; -import LoginScreen from './containers/LoginScreen'; -import Dashboard from './containers/Dashboard'; -import { LoginDetailsProvider } from './context/LoginDetailsContext'; +import { HashRouter as Router } from 'react-router-dom'; +import TabContainer from './containers/TabContainer'; +import { TabProvider } from './context/TabContext'; const theme = createMuiTheme({ palette: { @@ -15,6 +13,7 @@ const theme = createMuiTheme({ primary: { light: '#769CFF', main: '#769CFF', + dark: '#404040', // Dark 1 contrastText: '#FFFFFF', }, text: { @@ -24,13 +23,13 @@ const theme = createMuiTheme({ }, background: { default: '#404040', // Dark 2 - paper: '#4B4B4B', // Dark 3 + paper: '#404040', // Dark 1 }, }, typography: { fontFamily: ['Raleway', 'Arial'].join(','), button: { - textTransform: 'capitalize', + textTransform: 'initial', }, subtitle2: { color: 'rgba(255, 255, 255, 0.7)', @@ -59,18 +58,11 @@ export default function App() { return ( <div className="content"> <ThemeProvider theme={theme}> - <LoginDetailsProvider> - <Router> - <Switch> - <Route exact path={routes.login}> - <LoginScreen /> - </Route> - <Route path={routes.dashboard}> - <Dashboard /> - </Route> - </Switch> - </Router> - </LoginDetailsProvider> + <Router> + <TabProvider> + <TabContainer /> + </TabProvider> + </Router> </ThemeProvider> </div> ); diff --git a/app/QueryOutput.ts b/app/QueryOutput.ts index feaec6bacdec06713e081dcac2210de9440a22b6..9cb62e1427167d44a04799a5df47e67291d28f4b 100644 --- a/app/QueryOutput.ts +++ b/app/QueryOutput.ts @@ -3,4 +3,5 @@ import { Result } from '../backend/utils/mysql'; export interface QueryOutput { results?: Result; error?: undefined; + columnWidths?: number[]; } diff --git a/app/app.global.css b/app/app.global.css index 55a420d6b0ba5e7e880e01f2ca765cf33ff7adb4..72a70f846a7f79920c91dcdcb6a285573426d03b 100644 --- a/app/app.global.css +++ b/app/app.global.css @@ -36,7 +36,7 @@ body #root { /* Track */ ::-webkit-scrollbar-track { - background: #404040; + background: #4b4b4b; border-radius: 8px; } diff --git a/app/components/ErrorView.tsx b/app/components/ErrorView.tsx deleted file mode 100644 index 2a4e789a77abcedbf08a50bfe71cd81818a1ff2b..0000000000000000000000000000000000000000 --- a/app/components/ErrorView.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Paper, Typography } from '@material-ui/core'; -import { makeStyles } from '@material-ui/core/styles'; -import React, { useContext } from 'react'; -import { RecordingContext } from '../context/RecordingContext'; - -export default function ErrorView() { - const recordingContext = useContext(RecordingContext); - const error = recordingContext?.activeRecording?.error; - - const useStyles = makeStyles((theme) => ({ - content: { - display: 'flex', - flexFlow: 'column nowrap', - padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`, - }, - })); - - const classes = useStyles(); - - return ( - <div> - {error && ( - <Paper className={classes.content} elevation={1}> - <Typography variant="h5">Errors</Typography> - <Typography - variant="subtitle1" - style={{ - whiteSpace: 'pre-line', - }} - color="error" - > - {error} - </Typography> - </Paper> - )} - </div> - ); -} diff --git a/app/components/IDInfo.tsx b/app/components/IDInfo.tsx deleted file mode 100644 index 1fe032c8c0dce173baf80046c5bd97c5a4e537b9..0000000000000000000000000000000000000000 --- a/app/components/IDInfo.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { makeStyles, Paper, Typography } from '@material-ui/core'; -import SqlManagerSingleton from '../../backend/recorder/SqlManagerSingleton'; - -const useStyles = makeStyles((theme) => ({ - wrapper: { - padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`, - }, -})); - -export default function IDInfo() { - const manager = SqlManagerSingleton.getInstance(); - - const classes = useStyles(); - - return ( - <Paper elevation={1} className={classes.wrapper}> - <Typography variant="subtitle2"> - {`Runner ConnectionID: ${manager.runner?.connectionID} Runner ThreadID: ${manager.runner?.threadID}`} - </Typography> - <Typography variant="subtitle2"> - {`Monitor ConnectionID: ${manager.monitor?.connectionID} Monitor ThreadID: ${manager.monitor?.threadID}`} - </Typography> - <Typography variant="subtitle2"> - {`Agent ConnectionID: ${manager.agent?.connectionID} Agent ThreadID: ${manager.agent?.threadID}`} - </Typography> - </Paper> - ); -} diff --git a/app/components/TreeViewExplainAnalyze.tsx b/app/components/TreeViewExplainAnalyze.tsx deleted file mode 100644 index 2d80da70e94b33a2379ac5adad8c9e5dab9a589e..0000000000000000000000000000000000000000 --- a/app/components/TreeViewExplainAnalyze.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useContext } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import TreeView from '@material-ui/lab/TreeView'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import ChevronRightIcon from '@material-ui/icons/ChevronRight'; -import TreeItem from '@material-ui/lab/TreeItem'; -import { RecordingContext } from '../context/RecordingContext'; -import { ExplainAnalyzeNode } from '../../backend/data-processor/types/ExplainAnalyzeNode'; - -const useStyles = makeStyles({ - root: { - flexGrow: 1, - }, -}); - -export default function RecursiveTreeView() { - const recordingContext = useContext(RecordingContext); - const data = recordingContext?.activeRecording?.explainAnalyzeTree; - - const classes = useStyles(); - - const RenderTree = (nodes: ExplainAnalyzeNode) => ( - <TreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name}> - {Array.isArray(nodes.children) - ? nodes.children.map((node) => RenderTree(node)) - : null} - </TreeItem> - ); - - const RenderTreeWithoutRoot = (root?: ExplainAnalyzeNode) => { - // Renders only the children of root - const elements: JSX.Element[] = []; - root?.children.forEach((child) => { - elements.push(RenderTree(child)); - }); - return elements; - }; - - // TODO: Find dynamic solution to defaultExpanded - return ( - <TreeView - className={classes.root} - defaultCollapseIcon={<ExpandMoreIcon />} - defaultExpanded={['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']} - defaultExpandIcon={<ChevronRightIcon />} - > - {[RenderTreeWithoutRoot(data)]} - </TreeView> - ); -} diff --git a/app/components/ColorPicker.tsx b/app/components/dashboard/ColorPicker.tsx similarity index 100% rename from app/components/ColorPicker.tsx rename to app/components/dashboard/ColorPicker.tsx diff --git a/app/components/dashboard/ErrorView.tsx b/app/components/dashboard/ErrorView.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2417d04f24cbe2dbac973feda83b52161be1b34a --- /dev/null +++ b/app/components/dashboard/ErrorView.tsx @@ -0,0 +1,52 @@ +import { Paper, Typography } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import React, { useContext } from 'react'; +import { TabContext } from '../../context/TabContext'; + +const useStyles = makeStyles((theme) => ({ + content: { + display: 'flex', + flexFlow: 'column nowrap', + padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`, + }, +})); + +export default function ErrorView() { + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = { ...tab?.recordings }; + const error = recordings[tab?.activeRecordingID || '']?.error; + + let errorMessage; + if (error?.info) { + const errorcode = JSON.stringify(error?.info?.code, undefined, 2); + const errormsg = JSON.stringify(error?.info?.msg, undefined, 2); + errorMessage = `Errorcode: ${errorcode}\n${errormsg}`; + } else if (error === 'cancelled') { + errorMessage = 'Query was cancelled'; + } else { + errorMessage = String(error); + } + + const classes = useStyles(); + + return ( + <div> + {error && ( + <Paper className={classes.content} elevation={1}> + <Typography variant="h5">Errors</Typography> + <Typography + variant="subtitle1" + style={{ + whiteSpace: 'pre-line', + }} + color="error" + > + {errorMessage} + </Typography> + </Paper> + )} + </div> + ); +} diff --git a/app/components/dashboard/GlobalSnackbar.tsx b/app/components/dashboard/GlobalSnackbar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..439ab11db2227a47e8e2f81f52431b051a14d09a --- /dev/null +++ b/app/components/dashboard/GlobalSnackbar.tsx @@ -0,0 +1,24 @@ +import React, { useContext } from 'react'; +import { Snackbar } from '@material-ui/core'; +import { Alert } from '@material-ui/lab'; +import { GlobalSnackbarContext } from '../../context/GlobalSnackbarContext'; + +export default function GlobalSnackbar() { + const globalSnackbarContext = useContext(GlobalSnackbarContext); + + return ( + <Snackbar + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + autoHideDuration={5000} + open={globalSnackbarContext?.popupOpen} + onClose={() => globalSnackbarContext?.setPopupOpen(false)} + > + <Alert + severity={globalSnackbarContext?.popupSeverity} + onClose={() => globalSnackbarContext?.setPopupOpen(false)} + > + {globalSnackbarContext?.popupMessage} + </Alert> + </Snackbar> + ); +} diff --git a/app/components/dashboard/IDInfo.tsx b/app/components/dashboard/IDInfo.tsx new file mode 100644 index 0000000000000000000000000000000000000000..818a9eeaad7c76d193ec87ec4d5934f8e7188fd7 --- /dev/null +++ b/app/components/dashboard/IDInfo.tsx @@ -0,0 +1,61 @@ +import React, { useContext } from 'react'; +import { makeStyles, Paper, Typography, Tooltip } from '@material-ui/core'; +import { TabContext } from '../../context/TabContext'; + +const useStyles = makeStyles((theme) => ({ + wrapper: { + padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`, + }, + line: { + display: 'flex', + flexFlow: 'row nowrap', + justifyContent: 'flex-start', + alignItems: 'center', + }, + itemTitle: { + marginRight: theme.spacing(1), + }, +})); + +export default function IDInfo() { + const { state: tabState } = useContext(TabContext); + const { activeTabID, manager } = tabState; + const connection = manager.connections[activeTabID]; + + const classes = useStyles(); + + return ( + <Paper elevation={1} className={classes.wrapper}> + <Tooltip title="Runner is responsible for running queries which will be recorded"> + <div className={classes.line}> + <Typography className={classes.itemTitle} variant="subtitle1"> + Runner + </Typography> + <Typography variant="subtitle2"> + {`ConnectionID: ${connection?.runner?.connectionID} ThreadID: ${connection?.runner?.threadID}`} + </Typography> + </div> + </Tooltip> + <Tooltip title="Monitor is responsible for monitoring the resource usage of another session"> + <div className={classes.line}> + <Typography className={classes.itemTitle} variant="subtitle1"> + Monitor + </Typography> + <Typography variant="subtitle2"> + {`ConnectionID: ${connection?.monitor?.connectionID}, ThreadID: ${connection?.monitor?.threadID}`} + </Typography> + </div> + </Tooltip> + <Tooltip title="Agent is responsible for performing actions when Runner and Monitor are busy"> + <div className={classes.line}> + <Typography className={classes.itemTitle} variant="subtitle1"> + Agent + </Typography> + <Typography variant="subtitle2"> + {`ConnectionID: ${connection?.agent?.connectionID} ThreadID: ${connection?.agent?.threadID}`} + </Typography> + </div> + </Tooltip> + </Paper> + ); +} diff --git a/app/components/QueryRecorder.tsx b/app/components/dashboard/QueryRecorder.tsx similarity index 50% rename from app/components/QueryRecorder.tsx rename to app/components/dashboard/QueryRecorder.tsx index dcd30ecf762c3c4e5150cded5323955e8278b399..84eadac122f2bd0e9a9dfb23ad77179b80f9bb07 100644 --- a/app/components/QueryRecorder.tsx +++ b/app/components/dashboard/QueryRecorder.tsx @@ -1,4 +1,5 @@ -import React, { useState, useContext } from 'react'; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import React, { useContext } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import { Paper, @@ -9,18 +10,14 @@ import { Checkbox, CircularProgress, Slider, - IconButton, - Icon, - Snackbar, } from '@material-ui/core'; import Switch from '@material-ui/core/Switch'; import Collapse from '@material-ui/core/Collapse'; import clsx from 'clsx'; -import { v4 as uuid } from 'uuid'; -import { RecordingContext } from '../context/RecordingContext'; -import SqlManagerSingleton from '../../backend/recorder/SqlManagerSingleton'; -import { RawRecording } from '../../backend/recorder/SqlManager'; +import { RawRecording } from '../../../backend/recorder/SqlManager'; import ColorPicker from './ColorPicker'; +import { TabContext } from '../../context/TabContext'; +import { RecordingUpdate } from '../../types/RecordingUpdate'; const useStyles = makeStyles((theme) => ({ container: { @@ -58,127 +55,137 @@ const useStyles = makeStyles((theme) => ({ const defaultTimestep = 200; // ms -export default function QueryRecorder() { - const [query, setQuery] = useState(''); - const [explainAnalyze, setExplainAnalyze] = useState(false); - const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); - const [timeStep, setTimeStepValue] = useState( - (defaultTimestep as number) / 1000 - ); - const [recordingLabel, setRecordingLabel] = useState(''); - const [recordingColor, setRecordingColor] = useState(''); - const [waitRecording, setWaitRecording] = useState(false); - const [userQueryIsRunning, setUserQueryIsRunning] = useState(false); - const [managerState, setManagerState] = useState(''); - const [donePopupOpen, setDonePopupOpen] = useState(false); - const [recordingTime, setRecordingTime] = useState(0); +interface QuerRecorderProps { + onNewRecording: (recording: RecordingUpdate) => void; +} + +export default function QueryRecorder(props: QuerRecorderProps) { + const { onNewRecording } = props; - const manager = SqlManagerSingleton.getInstance(); - const recordingContext = useContext(RecordingContext); + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, tabs, manager } = tabState; + const tab = tabs[activeTabID]; - function updateContext( - result: RawRecording, - elapsed: number, - label: string, - color: string, - error?: string - ) { - const uniqueID = uuid(); - recordingContext?.recordingListItems?.forEach((listItem) => { - listItem.viewing = false; + function changeRecordingState(state: string) { + dispatch({ + type: 'SET_RECORDING_STATE', + payload: { tabID: activeTabID, value: state }, }); - const existingListItems = recordingContext?.recordingListItems || []; - const existingRecordings = recordingContext?.recordings || []; - const newRecording = { - queryOutput: result.result, - error, - stageTimes: !error ? result.stageTimes : undefined, - chartData: !error ? result.memoryPerformance : undefined, - optimizerTrace: !error ? result.optimizerTrace : undefined, - chartColors: !error ? result.chartColors : undefined, - explainAnalyze: - explainAnalyze || query.toLowerCase().includes('explain analyze'), - explainAnalyzeTree: !error ? result.explainAnalyze : undefined, - label, - uuid: uniqueID, - }; - const newListItem = { - query, + } + + function changeQuery(query: string) { + dispatch({ + type: 'SET_INPUT_QUERY', + payload: { tabID: activeTabID, value: query }, + }); + } + + function changeShowAdvancedOptions(show: boolean) { + dispatch({ + type: 'SET_SHOW_ADVANCED_OPTIONS', + payload: { tabID: activeTabID, value: show }, + }); + } + + function changeExplainAnalyze(explainAnalyze: boolean) { + dispatch({ + type: 'SET_EXPLAIN_ANALYZE', + payload: { tabID: activeTabID, value: explainAnalyze }, + }); + } + + function changeTimestep(timestep: number) { + dispatch({ + type: 'SET_TIMESTEP', + payload: { tabID: activeTabID, value: timestep / 1000 }, + }); + } + + function changeRecordingLabel(label: string) { + dispatch({ + type: 'SET_RECORDING_INPUT_LABEL', + payload: { tabID: activeTabID, value: label }, + }); + } + + function changeRecordingColor(color: string) { + dispatch({ + type: 'SET_RECORDING_INPUT_COLOR', + payload: { tabID: activeTabID, value: color }, + }); + } + + function changeQueryRunning(isRunning: boolean) { + dispatch({ + type: 'SET_QUERY_RUNNING', + payload: { tabID: activeTabID, value: isRunning }, + }); + } + + function changeRecordingRunning(isRunning: boolean) { + dispatch({ + type: 'SET_RECORDING_RUNNING', + payload: { tabID: activeTabID, value: isRunning }, + }); + } + + function updateContext(result?: RawRecording, elapsed?: number, error?: any) { + if (error && String(error).includes('cancelled')) { + return; + } + const update = { + result, elapsed, - label, - color, - uuid: uniqueID, - viewing: true, + label: tab.inputLabel, + color: tab.inputColor, + error, + explainAnalyze: tab.explainAnalyze, + query: tab.inputQuery, + tabID: result?.tabID, }; - recordingContext?.setActiveRecording(newRecording); - recordingContext?.setRecordingListItems([ - newListItem, - ...existingListItems, - ]); - recordingContext?.setRecordings([newRecording, ...existingRecordings]); - setDonePopupOpen(true); + onNewRecording(update); } async function record() { - setWaitRecording(true); - if (!manager.client) { + changeRecordingRunning(true); + if (!manager.connections[activeTabID].client) { console.error('QueryRecorder: Not connected.'); - setWaitRecording(false); + changeRecordingRunning(false); return; } try { // TODO: Get time elapsed from result const t0 = performance.now(); const result = await manager.record( - query, - setUserQueryIsRunning, - timeStep, - explainAnalyze, - setManagerState + tab.inputQuery, + activeTabID, + changeQueryRunning, + changeRecordingState, + tab.timestep, + tab.explainAnalyze ); const t1 = performance.now(); - setRecordingTime(t1 - t0); if (result?.error) { - let error; - if (result.error?.info) { - const errorcode = JSON.stringify( - result?.error?.info?.code, - undefined, - 2 - ); - const errormsg = JSON.stringify( - result?.error?.info?.msg, - undefined, - 2 - ); - error = `Errorcode: ${errorcode}\n${errormsg}`; - } else if (result.error === 'cancelled') { - error = 'Query was cancelled'; - } else { - error = String(result.error); - } - updateContext(result, t1 - t0, recordingLabel, recordingColor, error); + updateContext(result, t1 - t0, result?.error); } else { - updateContext(result, t1 - t0, recordingLabel, recordingColor); + updateContext(result, t1 - t0); } - setRecordingLabel(''); } catch (error) { console.error(`QueryRecorder: Error: ${error}`); - } finally { - setWaitRecording(false); + updateContext(undefined, undefined, error); } } const classes = useStyles(); function checkShortcut(e: { key: string; ctrlKey: boolean }) { - if (e.key === 'Enter' && e.ctrlKey) { + if (e.key === 'Enter' && e.ctrlKey && !tab.recordingRunning) { record(); } } function handleSliderChange(_: unknown, newTimeStep: number | number[]) { - setTimeStepValue((newTimeStep as number) / 1000); + changeTimestep(newTimeStep as number); } function getSliderValueText(value: number) { @@ -197,7 +204,13 @@ export default function QueryRecorder() { ]; function toggleAdvancedOptions() { - setShowAdvancedOptions((prev) => !prev); + changeShowAdvancedOptions(!tab.showAdvancedOptions); + } + + async function cancelRecording() { + await manager.cancelRecording(activeTabID); + changeQueryRunning(false); + changeRecordingRunning(false); } return ( @@ -219,8 +232,8 @@ export default function QueryRecorder() { label="Query" rows={2} rowsMax={10} - value={query} - onChange={(event) => setQuery(event.target.value)} + value={tab.inputQuery} + onChange={(event) => changeQuery(event.target.value)} onKeyUp={checkShortcut} helperText="Ctrl + Enter: Run" /> @@ -230,8 +243,8 @@ export default function QueryRecorder() { control={ <Checkbox color="primary" - checked={explainAnalyze} - onChange={(event) => setExplainAnalyze(event.target.checked)} + checked={tab.explainAnalyze} + onChange={(event) => changeExplainAnalyze(event.target.checked)} /> } label="Explain analyze" @@ -241,7 +254,7 @@ export default function QueryRecorder() { <FormControlLabel control={ <Switch - checked={showAdvancedOptions} + checked={tab.showAdvancedOptions} onChange={toggleAdvancedOptions} onKeyUp={(event) => event.key === 'Enter' && toggleAdvancedOptions() @@ -254,7 +267,7 @@ export default function QueryRecorder() { /> </div> <div className={classes.element}> - <Collapse in={showAdvancedOptions}> + <Collapse in={tab.showAdvancedOptions}> <div className={classes.element}> <div> <Typography variant="subtitle1"> @@ -279,21 +292,21 @@ export default function QueryRecorder() { <TextField variant="filled" label="Recording label" - value={recordingLabel} - onChange={(event) => setRecordingLabel(event.target.value)} + value={tab.inputLabel} + onChange={(event) => changeRecordingLabel(event.target.value)} onKeyUp={checkShortcut} /> </div> <ColorPicker - currentColor={recordingColor} - setColor={setRecordingColor} + currentColor={tab.inputColor} + setColor={changeRecordingColor} /> </Collapse> </div> <div className={clsx(classes.element, classes.recordButtonWrapper)}> <Button - disabled={waitRecording} + disabled={tab.recordingRunning} variant="contained" color="primary" onClick={() => record()} @@ -302,15 +315,15 @@ export default function QueryRecorder() { </Button> <Button style={{ margin: '8px' }} - disabled={!userQueryIsRunning} + disabled={!tab.queryRunning} variant="outlined" color="primary" - onClick={() => manager.cancelQuery()} + onClick={() => cancelRecording()} > Cancel </Button> - {waitRecording && ( + {tab.recordingRunning && ( <CircularProgress style={{ marginLeft: '16px' }} size="24px" @@ -318,30 +331,11 @@ export default function QueryRecorder() { /> )} </div> - {managerState.length > 0 && ( + {tab?.recordingState && tab.recordingState.length > 0 && ( <div className={classes.element}> - <Typography variant="subtitle2">{managerState}</Typography> + <Typography variant="subtitle2">{tab.recordingState}</Typography> </div> )} - <Snackbar - anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} - autoHideDuration={6000} - open={donePopupOpen} - onClose={() => setDonePopupOpen(false)} - message={`Recording finished in ${recordingTime.toFixed(1)} ms`} - action={ - <> - <IconButton - size="small" - aria-label="close" - color="inherit" - onClick={() => setDonePopupOpen(false)} - > - <Icon>close</Icon> - </IconButton> - </> - } - /> </Paper> </div> ); diff --git a/app/components/RecordingOptions/ChangeColor.tsx b/app/components/dashboard/RecordingOptions/ChangeColor.tsx similarity index 69% rename from app/components/RecordingOptions/ChangeColor.tsx rename to app/components/dashboard/RecordingOptions/ChangeColor.tsx index a666fe53a8c619d5da1d29cac47f1c16c65c29f1..db58a31720b9d965283de4496807bdbd5395a0a5 100644 --- a/app/components/RecordingOptions/ChangeColor.tsx +++ b/app/components/dashboard/RecordingOptions/ChangeColor.tsx @@ -1,19 +1,19 @@ import React, { useState, useContext } from 'react'; import { Dialog, DialogTitle, Button, MenuItem } from '@material-ui/core'; -import { RecordingContext } from '../../context/RecordingContext'; import ColorPicker from '../ColorPicker'; +import { TabContext } from '../../../context/TabContext'; export default function ChangeColor(props: { - recordinguuid: string; + recordingID: string; handleIconMenuClose: () => void; }) { - const { recordinguuid, handleIconMenuClose } = props; + const { recordingID, handleIconMenuClose } = props; const [changedColor, setChangedColor] = useState(''); const [changeColorDialogOpen, setChangeColorDialogOpen] = useState(false); - const recordingContext = useContext(RecordingContext); - const recordingListItems = recordingContext?.recordingListItems || []; + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID } = tabState; function handleChangeColorDialogClose() { setChangeColorDialogOpen(false); @@ -25,13 +25,10 @@ export default function ChangeColor(props: { } function handleChangeColorButtonClick() { - const newRecordingListItems = [...recordingListItems]; - newRecordingListItems.forEach((r) => { - if (r.uuid === recordinguuid) { - r.color = changedColor; - } + dispatch({ + type: 'SET_RECORDING_COLOR', + payload: { tabID: activeTabID, recordingID, value: changedColor }, }); - recordingContext?.setRecordingListItems(newRecordingListItems); handleChangeColorDialogClose(); } diff --git a/app/components/RecordingOptions/ChangeLabel.tsx b/app/components/dashboard/RecordingOptions/ChangeLabel.tsx similarity index 62% rename from app/components/RecordingOptions/ChangeLabel.tsx rename to app/components/dashboard/RecordingOptions/ChangeLabel.tsx index 612e07c209b549b507667a520530e615f7412957..1486a81ed80bf19202d891486510c50688190bcc 100644 --- a/app/components/RecordingOptions/ChangeLabel.tsx +++ b/app/components/dashboard/RecordingOptions/ChangeLabel.tsx @@ -6,19 +6,18 @@ import { Button, MenuItem, } from '@material-ui/core'; -import { RecordingContext } from '../../context/RecordingContext'; +import { TabContext } from '../../../context/TabContext'; export default function ChangeLabel(props: { - recordinguuid: string; + recordingID: string; handleIconMenuClose: () => void; }) { - const { recordinguuid, handleIconMenuClose } = props; + const { recordingID, handleIconMenuClose } = props; const [changedLabel, setChangedLabel] = useState(''); const [changeLabelDialogOpen, setChangeLabelDialogOpen] = useState(false); - const recordingContext = useContext(RecordingContext); - const recordingListItems = recordingContext?.recordingListItems || []; - const recordings = recordingContext?.recordings || []; + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID } = tabState; function handleChangeLabelDialogClose() { setChangeLabelDialogOpen(false); @@ -30,25 +29,20 @@ export default function ChangeLabel(props: { } function handleChangeLabelButtonClick() { - const newRecordingListItems = [...recordingListItems]; - newRecordingListItems.forEach((r) => { - if (r.uuid === recordinguuid) { - r.label = changedLabel; - } + dispatch({ + type: 'SET_RECORDING_LABEL', + payload: { tabID: activeTabID, recordingID, value: changedLabel }, }); - recordingContext?.setRecordingListItems(newRecordingListItems); - - const newRecordings = [...recordings]; - newRecordings.forEach((r) => { - if (r.uuid === recordinguuid) { - r.label = changedLabel; - } - }); - recordingContext?.setRecordings(newRecordings); handleChangeLabelDialogClose(); } + function checkShortcut(key: string) { + if (key === 'Enter') { + handleChangeLabelButtonClick(); + } + } + return ( <div> <MenuItem onClick={handleChangeLabelOptionClick}>Change label</MenuItem> @@ -63,6 +57,7 @@ export default function ChangeLabel(props: { label="New label" value={changedLabel} onChange={(event) => setChangedLabel(event.target.value)} + onKeyDown={(event) => checkShortcut(event.key)} /> <Button variant="contained" diff --git a/app/components/RecordingOptions/RecordingOptions.tsx b/app/components/dashboard/RecordingOptions/RecordingOptions.tsx similarity index 52% rename from app/components/RecordingOptions/RecordingOptions.tsx rename to app/components/dashboard/RecordingOptions/RecordingOptions.tsx index 785c3e351ff144fb18401fc65465074eaa580889..7b5bfea962e7b86cb18200dfe65fa76b1908ad51 100644 --- a/app/components/RecordingOptions/RecordingOptions.tsx +++ b/app/components/dashboard/RecordingOptions/RecordingOptions.tsx @@ -6,18 +6,27 @@ import { MenuItem, ListItemIcon, } from '@material-ui/core'; +import { valuesIn } from 'lodash'; import ChangeLabel from './ChangeLabel'; import ChangeColor from './ChangeColor'; -import { RecordingContext } from '../../context/RecordingContext'; +import { GlobalSnackbarContext } from '../../../context/GlobalSnackbarContext'; +import { TabContext } from '../../../context/TabContext'; -export default function RecordingOptions(props: { recordinguuid: string }) { +interface RecordingOptionsProps { + recordingID: string; +} + +export default function RecordingOptions(props: RecordingOptionsProps) { const [iconAnchorEl, setIconAnchorEl] = useState<null | HTMLElement>(null); - const { recordinguuid } = props; + const { recordingID } = props; - const recordingContext = useContext(RecordingContext); - const recordingListItems = recordingContext?.recordingListItems || []; - const recordings = recordingContext?.recordings || []; + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, tabs, manager } = tabState; + const tab = tabs[activeTabID]; + const recordingListItems = tab?.recordingListItems || {}; + + const globalSnackbarContext = useContext(GlobalSnackbarContext); function handleIconButtonClick(event: React.MouseEvent<HTMLButtonElement>) { setIconAnchorEl(event.currentTarget); @@ -28,35 +37,20 @@ export default function RecordingOptions(props: { recordinguuid: string }) { } function handleCopyQuery() { - const recordingListItem = recordingListItems.find( - (r) => r.uuid === recordinguuid - ); + const recordingListItem = recordingListItems[recordingID]; navigator.clipboard.writeText(recordingListItem?.query || ''); handleIconMenuClose(); + globalSnackbarContext?.setPopupMessage('Query copied to clipboard!'); + globalSnackbarContext?.setPopupSeverity('info'); + globalSnackbarContext?.setPopupOpen(true); } function handleRemoveRecording() { - const activeRecordinguuid = recordingContext?.activeRecording?.uuid || ''; - recordingContext?.setRecordingListItems( - recordingListItems.filter((r) => r.uuid !== recordinguuid) - ); - recordingContext?.setRecordings( - recordings.filter((r) => r.uuid !== recordinguuid) - ); - if (activeRecordinguuid === recordinguuid) { - const newActiveRecording = recordings.find( - (r) => r.uuid !== recordinguuid - ); - recordingContext?.setActiveRecording(newActiveRecording); - if (newActiveRecording) { - const newActiveRecordingListItem = recordingListItems.find( - (r) => r.uuid === newActiveRecording.uuid - ); - if (newActiveRecordingListItem) { - newActiveRecordingListItem.viewing = true; - } - } - } + dispatch({ + type: 'DELETE_RECORDING', + payload: { tabID: activeTabID, recordingID }, + }); + handleIconMenuClose(); } @@ -78,11 +72,11 @@ export default function RecordingOptions(props: { recordinguuid: string }) { onClose={handleIconMenuClose} > <ChangeLabel - recordinguuid={recordinguuid} + recordingID={recordingID} handleIconMenuClose={handleIconMenuClose} /> <ChangeColor - recordinguuid={recordinguuid} + recordingID={recordingID} handleIconMenuClose={handleIconMenuClose} /> <MenuItem onClick={handleCopyQuery}>Copy query</MenuItem> diff --git a/app/components/Recordings.tsx b/app/components/dashboard/Recordings.tsx similarity index 52% rename from app/components/Recordings.tsx rename to app/components/dashboard/Recordings.tsx index 7efb8e002c06b1d1ee2620ef5488b26bd97aebb5..fac486316c7d4269806b20ff8193e0faf42f355d 100644 --- a/app/components/Recordings.tsx +++ b/app/components/dashboard/Recordings.tsx @@ -8,13 +8,18 @@ import { Tooltip, Typography, } from '@material-ui/core'; +import React, { CSSProperties, useContext, useRef, useState } from 'react'; import InputAdornment from '@material-ui/core/InputAdornment'; import SearchIcon from '@material-ui/icons/Search'; -import React, { CSSProperties, useContext, useState } from 'react'; +import { keys, valuesIn } from 'lodash'; import { FixedSizeList } from 'react-window'; import useDeepCompareEffect from 'use-deep-compare-effect'; -import { RecordingContext } from '../context/RecordingContext'; +import { TabContext } from '../../context/TabContext'; +import { RecordingListItem } from '../../types/RecordingListItem'; import RecordingOptions from './RecordingOptions/RecordingOptions'; +import SaveRecording from './SaveRecording'; +import { GlobalSnackbarContext } from '../../context/GlobalSnackbarContext'; +import FileIO from '../../../backend/utils/FileIO'; const useStyles = makeStyles((theme) => ({ wrapper: { @@ -29,7 +34,6 @@ const useStyles = makeStyles((theme) => ({ textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden', - // textAlign: 'left', }, listItemTextRoot: { margin: 0, @@ -38,57 +42,102 @@ const useStyles = makeStyles((theme) => ({ listItemRoot: { padding: 0, }, + actionBar: { + display: 'flex', + flexFlow: 'row nowrap', + justifyContent: 'space-between', + }, })); export default function Recordings() { - const recordingContext = useContext(RecordingContext); - const recordings = recordingContext?.recordingListItems || []; + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = { ...tab?.recordingListItems }; - const [search, setSearch] = useState(''); const [displayedRecordings, setDisplayedRecordings] = useState(recordings); - const [activeRecording, setActiveRecording] = useState<string | undefined>( - recordings.find((recording) => recording.viewing)?.uuid - ); + const [activeRecordingID, setActiveRecordingID] = useState< + string | undefined + >(tab?.activeRecordingID); useDeepCompareEffect(() => { // Using useDeepCompareEffect because we want this to happen when 'recordings' // changes, even if it is an array (useEffect only supports primitive values) - const newActiveRecording = recordings.find((recording) => recording.viewing) - ?.uuid; - setActiveRecording(newActiveRecording); + const newActiveRecordingID = tab?.activeRecordingID; + setActiveRecordingID(newActiveRecordingID); setDisplayedRecordings(recordings); + console.log('Ran recordings update'); }, [recordings]); const classes = useStyles(); - function handleRecordingClick(index: number) { - if (index < displayedRecordings.length) { - const { uuid } = displayedRecordings[index]; - const originalIndex = recordings.findIndex( - (value) => value.uuid === uuid - ); - const newRecordingListItems = recordings; - if (activeRecording) { - const activeItemIndex = newRecordingListItems.findIndex( - (value) => value.uuid === activeRecording - ); - newRecordingListItems[activeItemIndex].viewing = false; - } - newRecordingListItems[originalIndex].viewing = true; - recordingContext?.setRecordingListItems(newRecordingListItems); - setActiveRecording(uuid); - if (recordingContext?.recordings) { - recordingContext?.setActiveRecording( - recordingContext.recordings[originalIndex] - ); + function handleRecordingClick(id: string) { + if (displayedRecordings[id]) { + let newActiveRecordingID = activeRecordingID; + setActiveRecordingID(id); + if (tab?.recordings) { + newActiveRecordingID = id; } + dispatch({ + type: 'SET_ACTIVE_RECORDING', + payload: { + tabID: activeTabID, + recordingID: newActiveRecordingID || '', + }, + }); } } - function handleSaveClick(index: number) { - // TODO: Implement - console.log(`Clicked ${index} button`); + const globalSnackbarContext = useContext(GlobalSnackbarContext); + + const inputRef = useRef<HTMLInputElement>(null); + + function openFileSelector() { + inputRef.current?.click(); + } + + function loadRecording() { + if (inputRef.current?.files) { + const file = inputRef.current.files[0]; + if (file) { + FileIO.loadRecording(file.path) + .then((response) => { + if (response) { + const tempChartData = response.chartData || []; + const chartDataLength = tempChartData?.length || 0; + const totalTime = + tempChartData[chartDataLength - 1]?.relative_time * 1000 || NaN; + const newListItem: RecordingListItem = { + query: response.query || 'Query lost', + elapsed: totalTime || response.elapsed || -1, + label: file.name, + color: '', + uuid: response.uuid, + viewing: true, + isSaved: true, + }; + response.label = file.name; + dispatch({ + type: 'ADD_RECORDING', + payload: { + tabID: tabState.activeTabID, + recording: response, + recordingListItem: newListItem, + recordingID: response.uuid, + }, + }); + } + return true; + }) + .catch((error) => { + console.error(error); + globalSnackbarContext?.setPopupMessage(String(error)); + globalSnackbarContext?.setPopupSeverity('error'); + globalSnackbarContext?.setPopupOpen(true); + }); + } + } } function formatElapsed(elapsed: number) { @@ -105,22 +154,24 @@ export default function Recordings() { return `${elapsed.toFixed(1)} ms`; } - function filterRecordings() { - const filtered = recordings.filter((recording) => - recording.label.toLowerCase().includes(search.toLowerCase()) - ); + function filterRecordings(query: string) { + let filtered: { [key: string]: RecordingListItem } = {}; + keys(recordings).forEach((id) => { + if (recordings[id].label.toLowerCase().includes(query.toLowerCase())) { + filtered[id] = recordings[id]; + } + }); + if (query === '') filtered = recordings; setDisplayedRecordings(filtered); } - function checkShortcut(e: { key: string }) { - if (e.key === 'Enter') { - filterRecordings(); - } - } + function renderRow(propss: { index: number; style: CSSProperties }) { + const { index, style } = propss; + const recording = valuesIn(displayedRecordings)[index]; - function renderRow(props: { index: number; style: CSSProperties }) { - const { index, style } = props; - const recording = displayedRecordings[index]; + if (!recording) { + return null; + } return ( <ListItem @@ -128,7 +179,7 @@ export default function Recordings() { style={style} key={index} button - selected={recording.uuid === activeRecording} + selected={recording.uuid === activeRecordingID} > <Tooltip title={index + 1}> <ListItemText @@ -151,7 +202,7 @@ export default function Recordings() { flexFlow: 'column nowrap', justifyContent: 'center', }} - onClick={() => handleRecordingClick(index)} + onClick={() => handleRecordingClick(recording.uuid)} /> </Tooltip> <Tooltip title={recording.label}> @@ -167,7 +218,7 @@ export default function Recordings() { flexBasis: 226, paddingRight: 4, }} - onClick={() => handleRecordingClick(index)} + onClick={() => handleRecordingClick(recording.uuid)} /> </Tooltip> <Tooltip title={formatElapsed(recording.elapsed)}> @@ -183,7 +234,7 @@ export default function Recordings() { flexBasis: 70, paddingRight: 4, }} - onClick={() => handleRecordingClick(index)} + onClick={() => handleRecordingClick(recording.uuid)} /> </Tooltip> <Tooltip title={recording.query}> @@ -194,17 +245,14 @@ export default function Recordings() { root: classes.listItemTextRoot, }} style={{ flexGrow: 1, flexShrink: 1, flexBasis: 150 }} - onClick={() => handleRecordingClick(index)} + onClick={() => handleRecordingClick(recording.uuid)} /> </Tooltip> - <Button - variant="outlined" - color="primary" - onClick={() => handleSaveClick(index)} - > - Save - </Button> - <RecordingOptions recordinguuid={recording.uuid} /> + <SaveRecording + recordinguuid={recording.uuid} + isSaved={recording.isSaved} + /> + <RecordingOptions recordingID={recording.uuid} /> </ListItem> ); } @@ -213,26 +261,49 @@ export default function Recordings() { <div className={classes.wrapper}> <Paper className={classes.content} elevation={1}> <Typography variant="h5">Recordings</Typography> - <TextField - id="outlined-search" - label="Search label..." - type="search" - variant="outlined" - value={search} - onChange={(event) => setSearch(event.target.value)} - onKeyUp={checkShortcut} - InputProps={{ - endAdornment: ( - <InputAdornment position="start"> - <SearchIcon /> - </InputAdornment> - ), - }} - style={{ - marginTop: 8, - width: 170, - }} - /> + <div className={classes.actionBar}> + <TextField + id="outlined-search" + label="Search label..." + type="search" + variant="outlined" + onChange={(event) => filterRecordings(event.target.value)} + onKeyPress={(event) => { + if (event.key === 'Enter') event.preventDefault(); + }} + InputProps={{ + endAdornment: ( + <InputAdornment position="start"> + <SearchIcon /> + </InputAdornment> + ), + }} + style={{ + marginTop: 8, + width: 170, + }} + /> + <div> + <input + type="file" + ref={inputRef} + style={{ display: 'none' }} + accept=".json" + onChange={() => loadRecording()} + /> + <Button + variant="outlined" + color="primary" + onClick={() => openFileSelector()} + style={{ + marginTop: 8, + height: 40, + }} + > + Load Recording + </Button> + </div> + </div> <ListItem classes={{ root: classes.listItemRoot }}> <ListItemText primary="#" @@ -298,7 +369,7 @@ export default function Recordings() { height={400} width="100%" itemSize={48} - itemCount={displayedRecordings.length} + itemCount={valuesIn(displayedRecordings).length} > {renderRow} </FixedSizeList> diff --git a/app/components/dashboard/SaveRecording.tsx b/app/components/dashboard/SaveRecording.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a1a4efc5896cb707cf3103338cabbfa4c3cfe99d --- /dev/null +++ b/app/components/dashboard/SaveRecording.tsx @@ -0,0 +1,75 @@ +import React, { useState, useContext } from 'react'; +import { Button, Dialog, DialogTitle, TextField } from '@material-ui/core'; +import FileIO from '../../../backend/utils/FileIO'; +import { GlobalSnackbarContext } from '../../context/GlobalSnackbarContext'; +import { TabContext } from '../../context/TabContext'; + +export default function SaveRecording(props: { + recordinguuid: string; + isSaved: boolean; +}) { + const { recordinguuid, isSaved } = props; + + const [btnDisabled, setBtnDisabled] = useState(isSaved); + const [filename, setFilename] = useState(''); + const [openFileDialog, setOpenFileDialog] = useState(false); + + const { state: tabState } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const { recordings, recordingListItems } = tab; + const recording = recordings[recordinguuid || '']; + const recordingListItem = recordingListItems[recordinguuid || '']; + + const globalSnackbarContext = useContext(GlobalSnackbarContext); + + async function handleSaveFile() { + if (filename && recording && recordingListItem) { + recording.isSaved = true; + recordingListItem.isSaved = true; + const path = await FileIO.saveRecordingDetails(recording, filename); + setOpenFileDialog(false); + setBtnDisabled(true); + globalSnackbarContext?.setPopupMessage(`Recording saved to ${path}`); + globalSnackbarContext?.setPopupSeverity('success'); + globalSnackbarContext?.setPopupOpen(true); + } + } + + function handleKeyUp(e: { key: string }) { + if (e.key === 'Enter') { + handleSaveFile(); + } + } + + return ( + <div> + <Button + variant="outlined" + color="primary" + disabled={btnDisabled} + onClick={() => setOpenFileDialog(true)} + > + Save + </Button> + <Dialog + open={openFileDialog} + onClose={() => setOpenFileDialog(false)} + aria-labelledby="form-dialog-title" + > + <DialogTitle id="form-dialog-title">Enter Filename</DialogTitle> + <TextField + autoFocus + variant="filled" + label="Filename" + value={filename} + onKeyUp={handleKeyUp} + onChange={(event) => setFilename(event.target.value)} + /> + <Button onClick={handleSaveFile} variant="contained" color="primary"> + Save + </Button> + </Dialog> + </div> + ); +} diff --git a/app/components/Login.tsx b/app/components/login/Login.tsx similarity index 83% rename from app/components/Login.tsx rename to app/components/login/Login.tsx index dbf33ab080f4ff5d6445a694cd9f9353843d40ab..42630725b6dc960af902065f658e7f8a7670211f 100644 --- a/app/components/Login.tsx +++ b/app/components/login/Login.tsx @@ -11,9 +11,10 @@ import { } from '@material-ui/core'; import { useHistory, useLocation } from 'react-router'; import clsx from 'clsx'; -import SqlManagerSingleton from '../../backend/recorder/SqlManagerSingleton'; -import routes from '../routes'; -import { LoginDetailsContext } from '../context/LoginDetailsContext'; +import routes from '../../routes'; +import { LoginDetailsContext } from '../../context/LoginDetailsContext'; +import { GlobalSnackbarContext } from '../../context/GlobalSnackbarContext'; +import { TabContext } from '../../context/TabContext'; const useStyles = makeStyles((theme) => ({ wrapper: { @@ -61,6 +62,9 @@ const DefaultProps = { export default function Login({ onConnect = undefined }: LoginProps) { const loginDetailsContext = useContext(LoginDetailsContext); + const snackBarContext = useContext(GlobalSnackbarContext); + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, displayTabs, manager, tabs } = tabState; const history = useHistory(); const location = useLocation(); @@ -88,20 +92,36 @@ export default function Login({ onConnect = undefined }: LoginProps) { async function handleConnectByLogin() { setConnecting(true); - const manager = SqlManagerSingleton.getInstance(); + const tab = tabs[activeTabID]; try { - await manager.connect( - { host, user, password, port }, + await manager.connect(activeTabID, tab?.serial, { + host, + user, + password, + port, database, - savePassword - ); + savePassword, + }); loginDetailsContext?.setLoginDetails({ - ...{ host, user, password: savePassword ? password : '', port }, + ...{ host, user, password, port }, database, savePassword, }); setErrorMessage(''); - + snackBarContext?.setPopupMessage('Connected successfully'); + snackBarContext?.setPopupSeverity('success'); + snackBarContext?.setPopupOpen(true); + const oldLabel = displayTabs[activeTabID].tabLabel; + if ( + oldLabel === 'New connection' || + oldLabel.includes(':') || + oldLabel === '' + ) { + dispatch({ + type: 'SET_DISPLAYTAB_LABEL', + payload: { id: activeTabID, label: `${host}:${database}` }, + }); + } if (location.pathname === routes.login) { history.push(routes.dashboard); } else if (onConnect) { @@ -131,7 +151,7 @@ export default function Login({ onConnect = undefined }: LoginProps) { Default port for X Protocol servers is 33060` ); } else { - setErrorMessage('Failed to connect'); + setErrorMessage(`Failed to connect: ${error}`); } setConnecting(false); @@ -144,8 +164,6 @@ export default function Login({ onConnect = undefined }: LoginProps) { } } - // TODO: Show result of connection (success/failure) - const classes = useStyles(); return ( diff --git a/app/components/ExplainAnalyzeTable.tsx b/app/components/results/ExplainAnalyzeTable.tsx similarity index 93% rename from app/components/ExplainAnalyzeTable.tsx rename to app/components/results/ExplainAnalyzeTable.tsx index 07cc3a257e0e9a69ba55173acfc9054424a7d0b0..27efd2f9b6eb4b75813757d103243509d46e0502 100644 --- a/app/components/ExplainAnalyzeTable.tsx +++ b/app/components/results/ExplainAnalyzeTable.tsx @@ -40,9 +40,11 @@ export default function ExplainAnalyzeTable(data: { node: any }) { ); } + rows.push(...node.additionalData); + return ( <TableContainer component={Paper}> - <Table className={classes.table} aria-label="simple table"> + <Table className={classes.table} size="small" aria-label="dense table"> <TableBody> {rows.map((row) => ( <TableRow key={row.description}> diff --git a/app/components/results/FlameGraphExplainAnalyze.tsx b/app/components/results/FlameGraphExplainAnalyze.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6c0c3d101c923a5095d4cf97fc9f472ed567c8ae --- /dev/null +++ b/app/components/results/FlameGraphExplainAnalyze.tsx @@ -0,0 +1,128 @@ +import { makeStyles, Typography } from '@material-ui/core'; +import React, { useContext, useRef, useState } from 'react'; +import { FlameGraph } from 'react-flame-graph'; +import { AutoSizer } from 'react-virtualized'; +import { ExplainAnalyzeNode } from '../../../backend/data-processor/types/ExplainAnalyzeNode'; +import { TabContext } from '../../context/TabContext'; +import ExplainAnalyzeTable from './ExplainAnalyzeTable'; +import useSmartTooltip from '../../smartToolTip'; + +const emptyNode: ExplainAnalyzeNode = { + id: '0', + offset: -1, + name: 'root', + time: '', + rows: -1, + value: 0, + timeFirstRow: 0, + timeAllRows: 0, + loops: -1, + additionalData: [], + children: [], +}; + +type ToolTipState = { + mouseX: number; + mouseY: number; + text: string; +}; + +function getMousePos(relativeContainer: any, mouseEvent: any) { + if (relativeContainer !== null) { + const rect = relativeContainer.getBoundingClientRect(); + const mouseX = mouseEvent.clientX - rect.left; + const mouseY = mouseEvent.clientY - rect.top; + return { mouseX, mouseY }; + } + return { mouseX: 0, mouseY: 0 }; +} + +const useStyles = makeStyles((theme) => ({ + header: { + padding: theme.spacing(1), + }, +})); + +export default function FlameGraphExplainAnalyze() { + // Might be a better way than using emptyNode? + const [selectedNode, setSelectedNode] = useState(emptyNode); + const [hoveredNode, setHoveredNode] = useState(emptyNode); + const [tooltipState, setTooltipState] = useState<ToolTipState | null>(null); + + const tooltipRef = useSmartTooltip({ + mouseX: tooltipState === null ? 0 : tooltipState.mouseX, + mouseY: tooltipState === null ? 0 : tooltipState.mouseY, + }); + + const classes = useStyles(); + const { state: tabState } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = { ...tab?.recordings }; + const activeRecordingID = tab?.activeRecordingID || ''; + const data = recordings[activeRecordingID]?.explainAnalyzeTree; + const containerRef = useRef(null); + console.log(data); + + return ( + <div> + <div className={classes.header}> + <Typography variant="h6">Explain Analyze Flame Graph</Typography> + </div> + <div ref={containerRef}> + <AutoSizer disableHeight> + {({ width }) => ( + <FlameGraph + data={data} + height={200} + width={width} + enableTooltips + disableDefaultTooltips + onChange={(node: { source: ExplainAnalyzeNode }) => { + setSelectedNode(node.source); + }} + onMouseOver={(_event: never, itemData: ExplainAnalyzeNode) => { + setHoveredNode(itemData); + setTooltipState({ + text: + itemData.name !== 'root' + ? `Actual time: ${itemData.time}` + : 'root', + ...getMousePos(containerRef.current, _event), + }); + }} + onMouseMove={(_event: never, itemData: ExplainAnalyzeNode) => { + setTooltipState({ + text: + itemData.name !== 'root' + ? `Actual time: ${itemData.time}` + : 'root', + ...getMousePos(containerRef.current, _event), + }); + }} + onMouseOut={() => { + setHoveredNode(selectedNode); + setTooltipState(null); + }} + /> + )} + </AutoSizer> + {tooltipState !== null && ( + <div + ref={tooltipRef} + style={{ + position: 'absolute', + zIndex: 3, + backgroundColor: '#000', + color: '#fff', + padding: '0.5rem', + }} + > + {tooltipState.text} + </div> + )} + </div> + <ExplainAnalyzeTable node={hoveredNode} /> + </div> + ); +} diff --git a/app/components/MemoryChart.tsx b/app/components/results/MemoryPerformance/MemoryChart.tsx similarity index 69% rename from app/components/MemoryChart.tsx rename to app/components/results/MemoryPerformance/MemoryChart.tsx index 5ae42c553cda33d08b7d7651c4168c9b22145f36..c9f94cbfbdcd5372d4a2665bbaaa3770de8c614e 100644 --- a/app/components/MemoryChart.tsx +++ b/app/components/results/MemoryPerformance/MemoryChart.tsx @@ -12,7 +12,7 @@ import { Typography, } from '@material-ui/core'; import _ from 'lodash'; -import React, { useState, useContext, CSSProperties } from 'react'; +import React, { useState, useContext, CSSProperties, useEffect } from 'react'; import { FixedSizeList } from 'react-window'; import { LineChart, @@ -25,7 +25,7 @@ import { ReferenceLine, Label, } from 'recharts'; -import { RecordingContext } from '../context/RecordingContext'; +import { TabContext } from '../../../context/TabContext'; const useStyles = makeStyles((theme) => ({ lineChart: { @@ -52,41 +52,61 @@ const useStyles = makeStyles((theme) => ({ }, })); -function getModalStyle() { - return { - top: '50%', - left: '50%', - transform: 'translate(-50%,-50%)', - }; -} +const modalStyle = { + top: '50%', + left: '50%', + transform: 'translate(-50%,-50%)', +}; export default function MemoryChart() { const classes = useStyles(); + const { state: tabState } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = tab?.recordings || {}; + const activeRecordingID = tab?.activeRecordingID || ''; + const stageTimes = recordings[activeRecordingID]?.stageTimes; + const chartData = recordings[activeRecordingID]?.chartData; + const eventColors = recordings[activeRecordingID]?.chartColors; + const top3List = recordings[activeRecordingID]?.top3Memory; + const [disabled, setDisabled] = useState<Array<string>>([]); const [validStages, setValidStages] = useState<Array<string>>([]); - // const validStages = [ - // 'stage/sql/preparing', - // 'stage/sql/optimizing', - // 'stage/sql/executing', - // 'stage/sql/end', - // ]; - const [modalStyle] = React.useState(getModalStyle); - const [openEvent, setOpenEvent] = React.useState(false); - const [openStage, setOpenStage] = React.useState(false); - const [checked, setChecked] = useState<Array<string>>([]); - const [filteredMemory, setFilteredMemory] = useState<Array<string>>([]); + const [openEvent, setOpenEvent] = useState(false); + const [openStage, setOpenStage] = useState(false); + const [eventsChecked, setEventsChecked] = useState<Array<string>>([]); + const [stagesChecked, setStagesChecked] = useState<Array<string>>([]); + const [filteredEvents, setFilteredEvents] = useState<Array<string>>([]); const [filteredStages, setFilteredStages] = useState<Array<string>>([]); + const [eventCheckAll, setEventCheckAll] = useState(true); + const [stageCheckAll, setStageCheckAll] = useState(false); - const recordingContext = useContext(RecordingContext); - const stageTimes = recordingContext?.activeRecording?.stageTimes; - const chartData = recordingContext?.activeRecording?.chartData; - const testColors = recordingContext?.activeRecording?.chartColors; + useEffect(() => { + if (top3List && chartData) { + const notTop3 = _.filter( + _.keys(chartData[0]).slice(1), + (o) => !_.includes(top3List, o) + ); + setDisabled(notTop3); + setEventsChecked(notTop3); + } + }, [top3List]); function getMaxTime() { // eslint-disable-next-line prefer-spread return Math.max.apply(Math, _.map(chartData, 'relative_time')); } + function formatBytes(byte: number) { + if (Math.abs(byte) > 10000000) { + return `${Math.floor(byte / 1024 / 1024)} MB`; + } + if (Math.abs(byte) > 10000) { + return `${Math.floor(byte / 1024)} KB`; + } + return `${Math.floor(byte)} B`; + } + // When a legend is clicked the datakey is added to the disabled list, which will remove it form the line chart function eventToggle(dataKey: string) { if (_.includes(disabled, dataKey)) { @@ -104,42 +124,68 @@ export default function MemoryChart() { } } - function formatBytes(byte: number) { - if (Math.abs(byte) > 10000000) { - return `${Math.floor(byte / 1024 / 1024)} MB`; - } - if (Math.abs(byte) > 10000) { - return `${Math.floor(byte / 1024)} KB`; + function handleEventToggle(value: string) { + const curIndex = eventsChecked.indexOf(value); + const newChecked = [...eventsChecked]; + + if (curIndex === -1) { + eventToggle(value); + newChecked.push(value); + } else { + eventToggle(value); + newChecked.splice(curIndex, 1); } - return `${Math.floor(byte)} B`; + setEventsChecked(newChecked); } - function handleToggle(value: string, memory: boolean) { - const curIndex = checked.indexOf(value); - const newChecked = [...checked]; + function handleStageToggle(value: string) { + const curIndex = stagesChecked.indexOf(value); + const newChecked = [...stagesChecked]; if (curIndex === -1) { - if (memory) { - eventToggle(value); - } else { - validStageToggle(value); - } + validStageToggle(value); newChecked.push(value); } else { - if (memory) { - eventToggle(value); - } else { - validStageToggle(value); - } + validStageToggle(value); newChecked.splice(curIndex, 1); } - setChecked(newChecked); + setStagesChecked(newChecked); + } + + // Turns on/off all events + function handleEventCheckAllToggle() { + if (eventCheckAll === true) { + setEventCheckAll(false); + if (chartData) { + setEventsChecked(_.keys(chartData[0]).slice(1)); + setDisabled(_.keys(chartData[0]).slice(1)); + } + } else { + setEventCheckAll(true); + setEventsChecked([]); + setDisabled([]); + } + } + + // Turns on/off all stages + function handleStageCheckAllToggle() { + if (stageCheckAll === true) { + setStageCheckAll(false); + setStagesChecked([]); + setValidStages([]); + } else { + setStageCheckAll(true); + if (stageTimes) { + setStagesChecked(_.map(_.uniqBy(stageTimes, 'stage'), 'stage')); + setValidStages(_.map(_.uniqBy(stageTimes, 'stage'), 'stage')); + } + } } // Opens the modal to select event function handleOpenEvent() { if (chartData) { - setFilteredMemory(_.keys(chartData[0]).slice(1)); + setFilteredEvents(_.keys(chartData[0]).slice(1)); } setOpenEvent(true); } @@ -160,7 +206,7 @@ export default function MemoryChart() { setOpenStage(false); } - // Sets filteredMemory with a list of the datakeys that matches the text in the search bar + // Sets filteredEvents with a list of the datakeys that matches the text in the search bar function searchBarFilter(search: string, memory: boolean) { let filteredList = ['']; if (memory) { @@ -169,7 +215,7 @@ export default function MemoryChart() { _.includes(o.toLowerCase(), search.toLowerCase()) ); } - setFilteredMemory(filteredList); + setFilteredEvents(filteredList); } else { if (stageTimes) { filteredList = _.filter( @@ -184,30 +230,30 @@ export default function MemoryChart() { function renderEventRow(props: { index: number; style: CSSProperties }) { const { index, style } = props; - const memoryType = filteredMemory[index]; + const eventType = filteredEvents[index]; - const labelId = `checkbox-list-label-${memoryType}`; + const labelId = `checkbox-list-label-${eventType}`; return ( <ListItem classes={{ root: classes.listItemRoot }} style={style} - key={memoryType} + key={eventType} role={undefined} dense button - onClick={() => handleToggle(memoryType, true)} + onClick={() => handleEventToggle(eventType)} > <ListItemIcon> <Checkbox edge="start" - checked={checked.indexOf(memoryType) === -1} + checked={eventsChecked.indexOf(eventType) === -1} tabIndex={-1} disableRipple inputProps={{ 'aria-labelledby': labelId }} color="primary" /> </ListItemIcon> - <ListItemText id={labelId} primary={`${memoryType}`} /> + <ListItemText id={labelId} primary={`${eventType}`} /> </ListItem> ); } @@ -226,12 +272,12 @@ export default function MemoryChart() { role={undefined} dense button - onClick={() => handleToggle(stage, false)} + onClick={() => handleStageToggle(stage)} > <ListItemIcon> <Checkbox edge="start" - checked={checked.indexOf(stage) !== -1} + checked={stagesChecked.indexOf(stage) !== -1} tabIndex={-1} disableRipple inputProps={{ 'aria-labelledby': labelId }} @@ -278,13 +324,25 @@ export default function MemoryChart() { label="Search event..." variant="filled" onChange={(e) => searchBarFilter(e.target.value, true)} + onKeyPress={(e) => { + if (e.key === 'Enter') e.preventDefault(); + }} /> </form> + <Checkbox + edge="start" + checked={eventCheckAll} + tabIndex={-1} + disableRipple + inputProps={{ 'aria-labelledby': 'eventCheckAll' }} + color="primary" + onClick={() => handleEventCheckAllToggle()} + /> <FixedSizeList height={400} width="100%" itemSize={48} - itemCount={filteredMemory.length} + itemCount={filteredEvents.length} > {renderEventRow} </FixedSizeList> @@ -316,8 +374,20 @@ export default function MemoryChart() { label="Search stage" variant="filled" onChange={(e) => searchBarFilter(e.target.value, false)} + onKeyPress={(e) => { + if (e.key === 'Enter') e.preventDefault(); + }} /> </form> + <Checkbox + edge="start" + checked={stageCheckAll} + tabIndex={-1} + disableRipple + inputProps={{ 'aria-labelledby': 'eventCheckAll' }} + color="primary" + onClick={() => handleStageCheckAllToggle()} + /> <FixedSizeList height={400} width="100%" @@ -330,6 +400,7 @@ export default function MemoryChart() { </Fade> </Modal> </div> + {chartData && chartData.length > 2 ? ( <div> <ResponsiveContainer height={400}> @@ -356,12 +427,14 @@ export default function MemoryChart() { /> <Tooltip formatter={(value) => formatBytes(Number(value))} + label="relative_time" + labelFormatter={(name) => `Time Taken: ${name}`} offset={150} /> {/* Creates an array from the key:value pairs in chartColors. The keys that are not disabled becomes a line */} - {_.toPairs(testColors) + {_.toPairs(eventColors) .filter((pair) => !_.includes(disabled, pair[0])) .map((pair) => ( <Line diff --git a/app/components/results/MemoryPerformance/MemoryEvents.tsx b/app/components/results/MemoryPerformance/MemoryEvents.tsx new file mode 100644 index 0000000000000000000000000000000000000000..adc5c10c755f1fa6808ae94590f50e3b2ae2431a --- /dev/null +++ b/app/components/results/MemoryPerformance/MemoryEvents.tsx @@ -0,0 +1,227 @@ +import React, { useContext, CSSProperties, useState } from 'react'; +import _ from 'lodash'; +import { FixedSizeList } from 'react-window'; +import { + Backdrop, + Button, + Checkbox, + Fade, + ListItem, + ListItemIcon, + ListItemText, + makeStyles, + Modal, + TextField, + Typography, +} from '@material-ui/core'; +import { MemoryPerformanceContext } from '../../../context/MemoryPerformanceContext'; +import { TabContext } from '../../../context/TabContext'; + +const useStyles = makeStyles((theme) => ({ + element: { + marginBottom: theme.spacing(1), + }, + paper: { + position: 'absolute', + width: 500, + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(2, 4, 3), + outline: 'none', + borderRadius: 4, + }, + listItemRoot: { + padding: 0, + }, +})); + +export default function MemoryEvents() { + const classes = useStyles(); + const memoryPerformanceContext = useContext(MemoryPerformanceContext); + const [disabled, setDisabled] = [ + memoryPerformanceContext?.disabled, + memoryPerformanceContext?.setDisabled, + ]; + const [openEvent, setOpenEvent] = [ + memoryPerformanceContext?.openEvent, + memoryPerformanceContext?.setOpenEvent, + ]; + const [eventsChecked, setEventsChecked] = [ + memoryPerformanceContext?.eventsChecked, + memoryPerformanceContext?.setEventsChecked, + ]; + const [filteredEvents, setFilteredEvents] = [ + memoryPerformanceContext?.filteredEvents, + memoryPerformanceContext?.setFilteredEvents, + ]; + const [allChecked, setAllChecked] = useState(false); + + const { state: tabState } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const { recordings, activeRecordingID } = tab; + const recording = recordings[activeRecordingID || '']; + const chartData = recording?.chartData; + + const modalStyle = { + top: '50%', + left: '50%', + transform: 'translate(-50%,-50%)', + }; + + // When a legend is clicked the datakey is added to the disabled list, which will remove it form the line chart + function eventToggle(dataKey: string) { + if (disabled) { + if (_.includes(disabled, dataKey)) { + setDisabled?.call( + undefined, + disabled.filter((obj) => obj !== dataKey) + ); + } else { + setDisabled?.call(undefined, disabled.concat(dataKey)); + } + } + } + + function handleEventToggle(value: string) { + const curIndex = eventsChecked?.indexOf(value); + const newChecked = [...(eventsChecked || '')]; + + if (curIndex === -1) { + eventToggle(value); + newChecked.push(value); + } else { + eventToggle(value); + if (curIndex !== undefined) { + newChecked.splice(curIndex, 1); + } + } + setEventsChecked?.call(undefined, newChecked); + } + + function handleEventCheckAllToggle() { + if (allChecked) { + setAllChecked(false); + if (chartData) { + setEventsChecked?.call(undefined, _.keys(chartData[0]).slice(1)); + setDisabled?.call(undefined, _.keys(chartData[0]).slice(1)); + } + } else { + setAllChecked(true); + setEventsChecked?.call(undefined, []); + setDisabled?.call(undefined, []); + } + } + + // Opens the modal to select event + function handleOpenEvent() { + if (chartData) { + setFilteredEvents?.call(undefined, _.keys(chartData[0]).slice(1)); + } + setOpenEvent?.call(undefined, true); + } + + function handleCloseEvent() { + setOpenEvent?.call(undefined, false); + } + + // Sets filteredEvents with a list of the datakeys that matches the text in the search bar + function searchBarFilter(search: string) { + let filteredList = ['']; + if (chartData) { + filteredList = _.filter(_.keys(chartData[0]).slice(1), (o) => + _.includes(o.toLowerCase(), search.toLowerCase()) + ); + } + setFilteredEvents?.call(undefined, filteredList); + } + + function renderEventRow(props: { index: number; style: CSSProperties }) { + const { index, style } = props; + + const eventType = filteredEvents ? filteredEvents[index] : ''; + + const labelId = `checkbox-list-label-${eventType}`; + return ( + <ListItem + classes={{ root: classes.listItemRoot }} + style={style} + key={eventType} + role={undefined} + dense + button + onClick={() => handleEventToggle(eventType)} + > + <ListItemIcon> + <Checkbox + edge="start" + checked={eventsChecked?.indexOf(eventType) === -1} + tabIndex={-1} + disableRipple + inputProps={{ 'aria-labelledby': labelId }} + color="primary" + /> + </ListItemIcon> + <ListItemText id={labelId} primary={`${eventType}`} /> + </ListItem> + ); + } + + return ( + <div> + {' '} + <Button + variant="outlined" + color="primary" + onClick={handleOpenEvent} + style={{ marginRight: 16 }} + > + Pick Events + </Button> + <Modal + open={openEvent !== undefined ? openEvent : false} + onClose={handleCloseEvent} + aria-labelledby="simple-modal-title" + closeAfterTransition + BackdropComponent={Backdrop} + BackdropProps={{ + timeout: 500, + }} + > + <Fade in={openEvent}> + <div style={modalStyle} className={classes.paper}> + <div className={classes.element}> + <Typography variant="h5">Pick events to show</Typography> + </div> + <form> + <TextField + id="filled-basic" + label="Search event..." + variant="filled" + onChange={(e) => searchBarFilter(e.target.value)} + /> + </form> + <Checkbox + edge="start" + checked={allChecked} + tabIndex={-1} + disableRipple + inputProps={{ 'aria-labelledby': 'allChecked' }} + color="primary" + onClick={() => handleEventCheckAllToggle()} + /> + <FixedSizeList + height={400} + width="100%" + itemSize={48} + itemCount={ + filteredEvents !== undefined ? filteredEvents?.length : 0 + } + > + {renderEventRow} + </FixedSizeList> + </div> + </Fade> + </Modal> + </div> + ); +} diff --git a/app/components/results/MemoryPerformance/MemoryPerformance.tsx b/app/components/results/MemoryPerformance/MemoryPerformance.tsx new file mode 100644 index 0000000000000000000000000000000000000000..470c3b8bb25a56e2c1363c6ca5fc6501a9822bcf --- /dev/null +++ b/app/components/results/MemoryPerformance/MemoryPerformance.tsx @@ -0,0 +1,170 @@ +import { makeStyles, Typography } from '@material-ui/core'; +import _ from 'lodash'; +import React, { useContext, useEffect } from 'react'; +import { + LineChart, + Line, + CartesianGrid, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + ReferenceLine, + Label, +} from 'recharts'; +import MemoryEvents from './MemoryEvents'; +import MemoryStages from './MemoryStages'; +import { MemoryPerformanceContext } from '../../../context/MemoryPerformanceContext'; +import { TabContext } from '../../../context/TabContext'; + +const useStyles = makeStyles((theme) => ({ + lineChart: { + zIndex: 999, + }, + buttonWrapper: { + display: 'flex', + flexFlow: 'row nowrap', + padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`, + }, +})); + +export default function MemoryPerformance() { + const classes = useStyles(); + + const memoryPerformanceContext = useContext(MemoryPerformanceContext); + const { state: tabState } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const { recordings, activeRecordingID } = tab; + const recording = recordings[activeRecordingID || '']; + const stageTimes = recording?.stageTimes; + const chartData = recording?.chartData; + const testColors = recording?.chartColors; + const top3List = recording?.top3Memory; + + const [eventsChecked, setEventsChecked] = [ + memoryPerformanceContext?.eventsChecked, + memoryPerformanceContext?.setEventsChecked, + ]; + const [disabled, setDisabled] = [ + memoryPerformanceContext?.disabled, + memoryPerformanceContext?.setDisabled, + ]; + + const validStages = memoryPerformanceContext?.validStages; + + useEffect(() => { + if (top3List && chartData) { + const notTop3 = _.filter( + _.keys(chartData[0]).slice(1), + (o) => !_.includes(top3List, o) + ); + setDisabled?.call(undefined, notTop3); + setEventsChecked?.call(undefined, notTop3); + } + }, [top3List]); + + function getMaxTime() { + // eslint-disable-next-line prefer-spread + return Math.max.apply(Math, _.map(chartData, 'relative_time')); + } + + function formatBytes(byte: number) { + if (Math.abs(byte) > 10000000) { + return `${Math.floor(byte / 1024 / 1024)} MB`; + } + if (Math.abs(byte) > 10000) { + return `${Math.floor(byte / 1024)} KB`; + } + return `${Math.floor(byte)} B`; + } + + return ( + <div className={classes.lineChart}> + <div className={classes.buttonWrapper}> + <div style={{ flexGrow: 1 }}> + <Typography variant="h6">Memory performance</Typography> + </div> + <MemoryEvents /> + <MemoryStages /> + </div> + + {chartData && chartData.length > 2 ? ( + <div> + <ResponsiveContainer height={400}> + <LineChart + width={800} + height={500} + data={chartData} + margin={{ top: 20, right: 30, left: 20, bottom: 5 }} + style={{ fontFamily: 'Roboto, sans-serif' }} + > + <CartesianGrid strokeDasharray="3 10" /> + {/* TODO: Create our own Axis tick component like this: http://recharts.org/en-US/examples/CustomizedLabelLineChart */} + <XAxis + dataKey="relative_time" + type="number" + domain={[0, getMaxTime()]} + tickCount={20} + stroke="#FFFFFF" + /> + <YAxis + type="number" + tickFormatter={formatBytes} + stroke="#FFFFFF" + /> + <Tooltip + formatter={(value) => formatBytes(Number(value))} + offset={150} + /> + + {/* Creates an array from the key:value pairs in chartColors. + The keys that are not disabled becomes a line */} + {_.toPairs(testColors) + .filter((pair) => !_.includes(disabled, pair[0])) + .map((pair) => ( + <Line + type="monotone" + dataKey={pair[0]} + key={pair[0]} + stroke={pair[1]} + dot={false} + /> + ))} + {/* Filters stageTimes to only include stages that are in validStages and only the first instance of them. + en creates reference lines from the data that remains */} + + {_.forEach(_.uniqBy(stageTimes, 'stage')) + ?.filter((stage) => _.includes(validStages, stage.stage)) + ?.map((item) => ( + <ReferenceLine + x={item.startTime} + stroke="#769CFF" + key={_.uniqueId('ref_')} + > + <Label + value={item.stage} + position="top" + style={{ color: '#FFFFFF' }} + /> + </ReferenceLine> + ))} + </LineChart> + </ResponsiveContainer> + </div> + ) : ( + <div> + {chartData ? ( + <Typography variant="subtitle1" style={{ padding: '8px 16px' }}> + There are not enough data points to show a meaningful graph + </Typography> + ) : ( + <Typography variant="subtitle1" style={{ padding: '8px 16px' }}> + No data + </Typography> + )} + </div> + )} + </div> + ); +} diff --git a/app/components/results/MemoryPerformance/MemoryStages.tsx b/app/components/results/MemoryPerformance/MemoryStages.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4521b6c9b34a9d05b23e60d923039fe8a4f13b46 --- /dev/null +++ b/app/components/results/MemoryPerformance/MemoryStages.tsx @@ -0,0 +1,230 @@ +import React, { useContext, CSSProperties, useState } from 'react'; +import _ from 'lodash'; +import { FixedSizeList } from 'react-window'; +import { + Backdrop, + Button, + Checkbox, + Fade, + ListItem, + ListItemIcon, + ListItemText, + makeStyles, + Modal, + TextField, + Typography, +} from '@material-ui/core'; +import { MemoryPerformanceContext } from '../../../context/MemoryPerformanceContext'; +import { TabContext } from '../../../context/TabContext'; + +const useStyles = makeStyles((theme) => ({ + element: { + marginBottom: theme.spacing(1), + }, + paper: { + position: 'absolute', + width: 500, + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(2, 4, 3), + outline: 'none', + borderRadius: 4, + }, + listItemRoot: { + padding: 0, + }, +})); + +export default function MemoryStages() { + const classes = useStyles(); + const memoryPerformanceContext = useContext(MemoryPerformanceContext); + const [validStages, setValidStages] = [ + memoryPerformanceContext?.validStages, + memoryPerformanceContext?.setValidStages, + ]; + const [openStage, setOpenStage] = [ + memoryPerformanceContext?.openStage, + memoryPerformanceContext?.setOpenStage, + ]; + const [stagesChecked, setStagesChecked] = [ + memoryPerformanceContext?.stagesChecked, + memoryPerformanceContext?.setStagesChecked, + ]; + const [filteredStages, setFilteredStages] = [ + memoryPerformanceContext?.filteredStages, + memoryPerformanceContext?.setFilteredStages, + ]; + const [allChecked, setAllChecked] = useState(false); + + const { state: tabState } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const { recordings, activeRecordingID } = tab; + const recording = recordings[activeRecordingID || '']; + const stageTimes = recording?.stageTimes; + + const modalStyle = { + top: '50%', + left: '50%', + transform: 'translate(-50%,-50%)', + }; + + function validStageToggle(stage: string) { + if (_.includes(validStages, stage) && validStages) { + setValidStages?.call( + undefined, + validStages?.filter((obj) => obj !== stage) + ); + } else if (validStages) { + setValidStages?.call(undefined, validStages?.concat(stage)); + } + } + + function handleStageToggle(value: string) { + const curIndex = stagesChecked?.indexOf(value); + const newChecked = [...(stagesChecked || '')]; + + if (curIndex === -1) { + validStageToggle(value); + newChecked.push(value); + } else { + validStageToggle(value); + + if (curIndex !== undefined) { + newChecked.splice(curIndex, 1); + } + } + setStagesChecked?.call(undefined, newChecked); + } + + function handleStageCheckAllToggle() { + if (allChecked) { + setAllChecked(false); + setStagesChecked?.call(undefined, []); + setValidStages?.call(undefined, []); + } else { + setAllChecked(true); + if (stageTimes) { + setStagesChecked?.call( + undefined, + _.map(_.uniqBy(stageTimes, 'stage'), 'stage') + ); + setValidStages?.call( + undefined, + _.map(_.uniqBy(stageTimes, 'stage'), 'stage') + ); + } + } + } + + // Opens the modal to select event + function handleOpenStage() { + if (stageTimes) { + setFilteredStages?.call( + undefined, + _.map(_.uniqBy(stageTimes, 'stage'), 'stage') + ); + } + setOpenStage?.call(undefined, true); + } + + function handleCloseStage() { + setOpenStage?.call(undefined, false); + } + + // Sets filteredEvents with a list of the datakeys that matches the text in the search bar + function searchBarFilter(search: string) { + let filteredList = ['']; + + if (stageTimes) { + filteredList = _.filter( + _.map(_.uniqBy(stageTimes, 'stage'), 'stage'), + (o) => _.includes(o.toLowerCase(), search.toLowerCase()) + ); + } + setFilteredStages?.call(undefined, filteredList); + } + + function renderStageRow(props: { index: number; style: CSSProperties }) { + const { index, style } = props; + + const stage = filteredStages ? filteredStages[index] : ''; + + const labelId = `checkbox-list-label-${stage}`; + return ( + <ListItem + classes={{ root: classes.listItemRoot }} + style={style} + key={stage} + role={undefined} + dense + button + onClick={() => handleStageToggle(stage)} + > + <ListItemIcon> + <Checkbox + edge="start" + checked={stagesChecked?.indexOf(stage) !== -1} + tabIndex={-1} + disableRipple + inputProps={{ 'aria-labelledby': labelId }} + color="primary" + /> + </ListItemIcon> + <ListItemText id={labelId} primary={`${stage}`} /> + </ListItem> + ); + } + return ( + <div> + {/* Stage modal */} + <Button variant="outlined" color="primary" onClick={handleOpenStage}> + Pick Stages + </Button> + <Modal + open={openStage !== undefined ? openStage : false} + onClose={handleCloseStage} + aria-labelledby="simple-modal-title" + closeAfterTransition + BackdropComponent={Backdrop} + BackdropProps={{ + timeout: 500, + }} + > + <Fade in={openStage}> + <div style={modalStyle} className={classes.paper}> + <div className={classes.element}> + <Typography variant="h5">Pick stages to show</Typography> + </div> + <form> + <TextField + id="filled-basic" + label="Search stage" + variant="filled" + onChange={(e) => searchBarFilter(e.target.value)} + /> + </form> + <Checkbox + edge="start" + checked={allChecked} + tabIndex={-1} + disableRipple + inputProps={{ 'aria-labelledby': 'eventCheckAll' }} + color="primary" + onClick={() => handleStageCheckAllToggle()} + /> + <FixedSizeList + height={400} + width="100%" + itemSize={48} + itemCount={ + filteredStages !== undefined ? filteredStages?.length : 0 + } + > + {renderStageRow} + </FixedSizeList> + </div> + </Fade> + </Modal> + </div> + ); +} diff --git a/app/components/OptimizerTrace.tsx b/app/components/results/OptimizerTrace.tsx similarity index 82% rename from app/components/OptimizerTrace.tsx rename to app/components/results/OptimizerTrace.tsx index 529d9c92e69700564767120bbaa740f4cdb5f672..815753ddfc338ba3c4e2b7460b8ab88df73ce9a1 100644 --- a/app/components/OptimizerTrace.tsx +++ b/app/components/results/OptimizerTrace.tsx @@ -2,7 +2,7 @@ import { Button, Typography } from '@material-ui/core'; import React, { useState, useContext } from 'react'; import ReactJson from 'react-json-view'; import { makeStyles } from '@material-ui/core/styles'; -import { RecordingContext } from '../context/RecordingContext'; +import { TabContext } from '../../context/TabContext'; export default function OptimizerTrace() { const [expandAll, setExpandAll] = useState<boolean | undefined>(undefined); @@ -28,13 +28,17 @@ export default function OptimizerTrace() { position: 'sticky', top: 0, zIndex: 1, - backgroundColor: '#4B4B4B', + backgroundColor: theme.palette.background.paper, }, trace: { zIndex: 0 }, })); - const recordingContext = useContext(RecordingContext); - const optimizerTrace = recordingContext?.activeRecording?.optimizerTrace; + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = tab?.recordings || {}; + const activeRecordingID = tab?.activeRecordingID || ''; + const optimizerTrace = recordings[activeRecordingID]?.optimizerTrace; function handleButtonClick() { if (typeof expandAll === 'undefined') { diff --git a/app/components/results/ResultGrid.tsx b/app/components/results/ResultGrid.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f17407776dd1ad0a97f35468e72f548559f0a88a --- /dev/null +++ b/app/components/results/ResultGrid.tsx @@ -0,0 +1,153 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable linebreak-style */ +import React, { CSSProperties, useContext, useEffect, useState } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import { Typography } from '@material-ui/core'; + +import { AutoSizer, MultiGrid } from 'react-virtualized'; +import { ResultValue } from '@mysql/xdevapi'; +import { TabContext } from '../../context/TabContext'; + +const useStyles = makeStyles((theme) => ({ + tableWrapper: { + minHeight: 200, + height: '100%', + width: '100%', + resize: 'vertical', + overflowY: 'hidden', + scrollbarWidth: 'none', + paddingBottom: theme.spacing(2), + }, + gridCell: { + whiteSpace: 'nowrap', + width: '100%', + height: '100%', + display: 'flex', + flexDirection: 'column', + paddingLeft: '1em', + paddingRight: '1em', + overflowX: 'auto', + justifyContent: 'center', + }, +})); + +export default function ResultGrid() { + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = tab?.recordings || {}; + const activeRecordingID = tab?.activeRecordingID || ''; + const result = recordings[activeRecordingID]?.queryOutput; + const classes = useStyles(); + + function formatDate(d: Date) { + let month = `${d.getMonth() + 1}`; + let day = `${d.getDate()}`; + const year = d.getFullYear(); + + if (month.length < 2) month = `0${month}`; + if (day.length < 2) day = `0${day}`; + + return [year, month, day].join('-'); + } + + function format(index: number, entry: unknown, types: number[]): string { + let datatype = 0; + if (types) { + datatype = types[index]; + } + if (datatype === 12) { + if (typeof entry === 'number') { + const date = new Date(Number(entry)); + return formatDate(date); + } + const date = new Date(String(entry)); + return formatDate(date); + } + return String(entry); + } + + if (!result || !result.results) { + return ( + <Typography style={{ padding: '8px 16px' }} variant="subtitle1"> + No data to show + </Typography> + ); + } + + const { labels, types, values } = result.results; + const { columnWidths } = result; + + const STYLE: CSSProperties = { + width: '100%', + fontFamily: 'raleway', + resize: 'none', + }; + + type CellRendererType = { + rowIndex: number; + columnIndex: number; + style: React.CSSProperties; + }; + + function cellRenderer({ rowIndex, columnIndex, style }: CellRendererType) { + const content = + rowIndex === 0 + ? labels[columnIndex] + : format(columnIndex, values[rowIndex - 1][columnIndex], types); + return ( + <div + style={style} + key={`${columnIndex}-${rowIndex}`} + className={classes.gridCell} + > + {content} + </div> + ); + } + + function getColumnWidth({ index }: { index: number }) { + console.log(columnWidths); + return Math.max(columnWidths ? columnWidths[index] : 100, 50); + } + + return ( + <div className={classes.tableWrapper}> + <div + style={{ + minHeight: 201, + height: '100%', + width: '100%', + paddingBottom: 1, + }} + > + <AutoSizer> + {({ height, width }) => ( + <MultiGrid + cellRenderer={cellRenderer} + columnWidth={getColumnWidth} + columnCount={labels.length} + height={height} + rowHeight={48} + rowCount={values.length + 1} + fixedRowCount={1} + width={width} + style={STYLE} + styleTopRightGrid={{ + fontWeight: 'bold', + width: '100%', + backgroundColor: '#404040', + fontSize: 14, + }} + styleBottomRightGrid={{ + outline: 'none', + }} + overscanColumnCount={0} + overscanRowCount={2} + /> + )} + </AutoSizer> + </div> + </div> + ); +} diff --git a/app/components/ResultJson.tsx b/app/components/results/ResultJson.tsx similarity index 100% rename from app/components/ResultJson.tsx rename to app/components/results/ResultJson.tsx diff --git a/app/components/ResultTable.tsx b/app/components/results/ResultTable.tsx similarity index 88% rename from app/components/ResultTable.tsx rename to app/components/results/ResultTable.tsx index 57638a6dbb93cc492d4434fc3c35859efe745ee1..1fb25530a78313a8452649d0134d2a2c7ec7e8ee 100644 --- a/app/components/ResultTable.tsx +++ b/app/components/results/ResultTable.tsx @@ -4,15 +4,16 @@ import { TableCell, Typography } from '@material-ui/core'; import { AutoSizer, Column, Table } from 'react-virtualized'; import { uniqueId } from 'lodash'; import clsx from 'clsx'; -import { RecordingContext } from '../context/RecordingContext'; +import { TabContext } from '../../context/TabContext'; const useStyles = makeStyles((theme) => ({ tableWrapper: { minHeight: 200, height: '100%', width: '100%', - resize: 'vertical', - overflowY: 'scroll', + resize: 'both', + overflowY: 'auto', + overflowX: 'hidden', scrollbarWidth: 'none', paddingBottom: theme.spacing(2), }, @@ -30,12 +31,10 @@ const useStyles = makeStyles((theme) => ({ flex: '1 1 !important', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - overflow: 'hidden', '& .ReactVirtualized__Table__rowColumn': { flex: '1 1 !important', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - overflow: 'hidden', }, }, tableRowHover: { @@ -47,7 +46,7 @@ const useStyles = makeStyles((theme) => ({ flex: '1 1 !important', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - overflow: 'hidden', + overflowY: 'hidden', }, })); @@ -63,12 +62,15 @@ const StyledTableCell = withStyles(() => )(TableCell); export default function ResultTable() { - const recordingContext = useContext(RecordingContext); + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = tab?.recordings || {}; + const activeRecordingID = tab?.activeRecordingID || ''; + const result = recordings[activeRecordingID]?.queryOutput; const classes = useStyles(); - const result = recordingContext?.activeRecording?.queryOutput; - if (!result || !result.results) { return ( <Typography style={{ padding: '8px 16px' }} variant="subtitle1"> @@ -77,7 +79,7 @@ export default function ResultTable() { ); } - const { labels, types, values } = result.results; + const { labels, types, values } = result?.results; function formatDate(d: Date) { let month = `${d.getMonth() + 1}`; @@ -152,6 +154,7 @@ export default function ResultTable() { headerHeight={48} gridStyle={{ direction: 'inherit', + outline: 'none', }} rowCount={values.length} estimatedColumnSize={100} diff --git a/app/components/results/TreeViewExplainAnalyze.tsx b/app/components/results/TreeViewExplainAnalyze.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6cde7db2253263edaa67e6990a0bc184fc64ca32 --- /dev/null +++ b/app/components/results/TreeViewExplainAnalyze.tsx @@ -0,0 +1,63 @@ +import React, { useContext } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import TreeView from '@material-ui/lab/TreeView'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import TreeItem from '@material-ui/lab/TreeItem'; +import { Typography } from '@material-ui/core'; +import { ExplainAnalyzeNode } from '../../../backend/data-processor/types/ExplainAnalyzeNode'; +import { TabContext } from '../../context/TabContext'; + +const useStyles = makeStyles((theme) => ({ + root: { + flexGrow: 1, + }, + header: { + padding: theme.spacing(1), + }, +})); + +export default function RecursiveTreeView() { + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = tab?.recordings || {}; + const activeRecordingID = tab?.activeRecordingID || ''; + const data = recordings[activeRecordingID]?.explainAnalyzeTree; + + const classes = useStyles(); + + const RenderTree = (nodes: ExplainAnalyzeNode) => ( + <TreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name}> + {Array.isArray(nodes.children) + ? nodes.children.map((node) => RenderTree(node)) + : null} + </TreeItem> + ); + + const RenderTreeWithoutRoot = (root?: ExplainAnalyzeNode) => { + // Renders only the children of root + const elements: JSX.Element[] = []; + root?.children.forEach((child) => { + elements.push(RenderTree(child)); + }); + return elements; + }; + + // TODO: Find dynamic solution to defaultExpanded + return ( + <div> + <div className={classes.header}> + <Typography variant="h6">Explain Analyze Tree View</Typography> + </div> + <TreeView + className={classes.root} + defaultCollapseIcon={<ExpandMoreIcon />} + // defaultExpanded={['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']} + defaultExpandIcon={<ChevronRightIcon />} + > + {[RenderTreeWithoutRoot(data)]} + </TreeView> + </div> + ); +} diff --git a/app/components/tabs/Tab.tsx b/app/components/tabs/Tab.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4605378da30467717a03ece32933897e72fad230 --- /dev/null +++ b/app/components/tabs/Tab.tsx @@ -0,0 +1,89 @@ +import { + Button, + Divider, + Icon, + IconButton, + makeStyles, + Tooltip, +} from '@material-ui/core'; +import React from 'react'; + +// Need to accept width before making styles, therefore a double arrow function +const useStyles = (width: number, active: boolean, canClose: boolean) => + makeStyles((theme) => ({ + wrapper: { + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + justifyItems: 'center', + flexGrow: 1, + flexShrink: 0, + flexBasis: width, + maxWidth: width, + }, + labelButton: { + flexGrow: 1, + maxWidth: width - 30 - 30 - 4, // option button, close button, divider margin + minWidth: 30, + }, + labelButtonText: { + maxWidth: width - 30 - 30 - 4 - 16, + color: active ? 'rgba(255,255,255,0.9)' : 'rgba(255,255,255,0.6)', + display: 'inline-block', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + overflow: 'hidden', + }, + closeButton: { + color: + active && canClose ? 'rgba(255,255,255,0.9)' : 'rgba(255,255,255,0.6)', + }, + iconButton: { + color: active ? 'rgba(255,255,255,0.9)' : 'rgba(255,255,255,0.6)', + }, + endDivider: { + marginLeft: 4, + }, + })); + +interface TabProps { + label: string; + uuid: string; + isActive: boolean; + width: number; + canClose: boolean; + onSwitch: (uuid: string) => void; + onClose: (uuid: string) => void; +} + +export default function Tab(props: TabProps) { + const { label, uuid, isActive, width, canClose, onSwitch, onClose } = props; + + // Passing in max width of a tab + const classes = useStyles(width, isActive, canClose)(); + + return ( + <div className={classes.wrapper}> + <Tooltip title={label} enterDelay={500} enterNextDelay={500}> + <Button + className={classes.labelButton} + classes={{ label: classes.labelButtonText }} + onClick={() => onSwitch(uuid)} + > + {label} + </Button> + </Tooltip> + <IconButton size="small"> + <Icon className={classes.iconButton}>more_vert</Icon> + </IconButton> + <IconButton + size="small" + onClick={() => onClose(uuid)} + disabled={!canClose || !isActive} + > + <Icon className={classes.closeButton}>clear</Icon> + </IconButton> + <Divider className={classes.endDivider} orientation="vertical" flexItem /> + </div> + ); +} diff --git a/app/components/tabs/TabBar.tsx b/app/components/tabs/TabBar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..256b65b163998b15c8a449f4c64370c8c3343044 --- /dev/null +++ b/app/components/tabs/TabBar.tsx @@ -0,0 +1,174 @@ +import { Icon, IconButton, makeStyles } from '@material-ui/core'; +import { keys, max, min, valuesIn } from 'lodash'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +import clsx from 'clsx'; +import { TabContext } from '../../context/TabContext'; +import Tab from './Tab'; +import noScrollStyles from './no-scrollbar.module.css'; + +const useStyles = (tabWidth: number, tabsWidth: number) => + makeStyles((theme) => ({ + wrapper: { + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + justifyItems: 'center', + maxHeight: 40, + backgroundColor: theme.palette.primary.dark, + }, + tabsWrapper: { + display: 'flex', + flexFlow: 'column nowrap', + alignItems: 'flex-start', + justifyItems: 'center', + maxHeight: 40, + maxWidth: tabsWidth + 38, + overflow: 'scroll', + }, + tabs: { + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + justifyItems: 'flex-start', + }, + endButtons: { + display: 'flex', + flexFlow: 'row nowrap', + flexGrow: 1, + justifyContent: 'space-between', + marginLeft: theme.spacing(1), + }, + activeIndicatorWrapper: { + display: 'inline-block', + // flexFlow: 'row nowrap', + width: '100%', + }, + activeIndicator: { + flexGrow: 1, + flexShrink: 1, + flexBasis: tabWidth, + maxWidth: tabWidth, + minWidth: tabWidth, + height: 2, + minHeight: 2, + marginTop: 1, + backgroundColor: theme.palette.primary.main, + transitionProperty: 'margin-left', + transitionDuration: '0.2s', + }, + })); + +export default function TabBar() { + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, displayTabs, tabs } = tabState; + const tab = tabs[activeTabID]; + + const [activeTabIndex, setActiveTabIndex] = useState(0); + const [width, setWidth] = useState(0); + // const [maximized, setMaximized] = useState(true); + + const wrapperRef = useRef<HTMLDivElement>(null); + const tabsWrapperRef = useRef<HTMLDivElement>(null); + + const availableWidth = width - 38 - 360; + const numberOfTabs = keys(displayTabs).length; + const fullTabWidth = 196; + const minTabWidth = 110; + const isFull = availableWidth < numberOfTabs * fullTabWidth; + + const tabWidth = !isFull + ? fullTabWidth + : max([ + min([availableWidth / keys(displayTabs).length, fullTabWidth]), + minTabWidth, + ]) || 0; + + useEffect(() => { + const tabIndex = activeTabID + ? valuesIn(displayTabs).findIndex((value) => value.tabID === activeTabID) + : 0; + setActiveTabIndex(tabIndex); + if (tabsWrapperRef.current && isFull) { + // Scroll to the active tab + // Item 0 is the div containing the tabs + // Item tabIndex is the active tab within item 0 + tabsWrapperRef.current.children + .item(0) + ?.children.item(tabIndex) + ?.scrollIntoView({ behavior: 'auto' }); + } + }, [activeTabID, displayTabs]); + + useEffect(() => { + const nextWidth = wrapperRef?.current + ? wrapperRef?.current?.offsetWidth + : 0; + setWidth(nextWidth); + }, [wrapperRef.current]); + + // function toggleMaximized() { + // const window = remote?.getCurrentWindow(); + // if (window?.isMaximized()) { + // window?.unmaximize(); + // setMaximized(false); + // } else { + // window?.maximize(); + // setMaximized(true); + // } + // } + + const classes = useStyles(tabWidth, availableWidth)(); + + return ( + <div + className={clsx(classes.wrapper, noScrollStyles.appbar)} + ref={wrapperRef} + > + <div + className={clsx(classes.tabsWrapper, noScrollStyles.tabswrapper)} + ref={tabsWrapperRef} + > + <div className={classes.tabs}> + {valuesIn(displayTabs).map((displayTab) => ( + <Tab + key={displayTab.tabID} + label={displayTab.tabLabel} + uuid={displayTab.tabID} + isActive={activeTabID === displayTab.tabID} + width={tabWidth} + canClose={ + valuesIn(displayTabs).length > 1 && !tab.recordingRunning + } + onSwitch={(id) => + dispatch({ type: 'SET_ACTIVE_TAB', payload: id }) + } + onClose={(id) => dispatch({ type: 'DELETE_TAB', payload: id })} + /> + ))} + </div> + <div className={classes.activeIndicatorWrapper}> + <div + className={classes.activeIndicator} + style={{ marginLeft: tabWidth * activeTabIndex }} + /> + </div> + </div> + <div className={classes.endButtons}> + <IconButton size="small" onClick={() => dispatch({ type: 'ADD_TAB' })}> + <Icon>add</Icon> + </IconButton> + {/* <div style={{ alignSelf: 'flex-end' }}> + <IconButton size="small" onClick={() => {}}> + <Icon>minimize</Icon> + </IconButton> + <IconButton size="small" onClick={() => toggleMaximized()}> + {maximized ? <Icon>fullscreen_exit</Icon> : <Icon>fullscreen</Icon>} + </IconButton> + <IconButton size="small" onClick={() => {}}> + <Icon>clear</Icon> + </IconButton> + </div> */} + </div> + </div> + ); +} diff --git a/app/components/tabs/css.d.ts b/app/components/tabs/css.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..af3d030048bbf98d7e4f7a52a75aff894e2aa0d5 --- /dev/null +++ b/app/components/tabs/css.d.ts @@ -0,0 +1,9 @@ +declare module '*.scss' { + const content: { [className: string]: string }; + export default content; +} + +declare module '*.css' { + const content: { [className: string]: string }; + export default content; +} diff --git a/app/components/tabs/no-scrollbar.module.css b/app/components/tabs/no-scrollbar.module.css new file mode 100644 index 0000000000000000000000000000000000000000..e3eaa0f2e51c25f6fa9c05834f646c7f615433fa --- /dev/null +++ b/app/components/tabs/no-scrollbar.module.css @@ -0,0 +1,7 @@ +.tabswrapper::-webkit-scrollbar { + display: none; +} + +.appbar { + -webkit-app-region: drag; +} diff --git a/app/containers/Dashboard.tsx b/app/containers/Dashboard.tsx index c719061ba04b177d61ccdf1430d987d0fcf23bf8..7fec91b7b24fa8228c1191987f1cac9bb56496bc 100644 --- a/app/containers/Dashboard.tsx +++ b/app/containers/Dashboard.tsx @@ -1,16 +1,18 @@ -import React, { useContext, useEffect, useState } from 'react'; +import React, { useContext, useState } from 'react'; import { Button, Dialog, makeStyles } from '@material-ui/core'; -import { useLocation, useHistory } from 'react-router'; import PanelGroup from 'react-panelgroup'; -import QueryRecorder from '../components/QueryRecorder'; -import routes from '../routes'; -import Login from '../components/Login'; -import { LoginDetailsContext } from '../context/LoginDetailsContext'; -import { RecordingProvider } from '../context/RecordingContext'; +import { v4 as uuid } from 'uuid'; +import QueryRecorder from '../components/dashboard/QueryRecorder'; +import Login from '../components/login/Login'; import ResultView from './ResultView'; -import Recordings from '../components/Recordings'; -import ErrorView from '../components/ErrorView'; -import IDInfo from '../components/IDInfo'; +import Recordings from '../components/dashboard/Recordings'; +import ErrorView from '../components/dashboard/ErrorView'; +import IDInfo from '../components/dashboard/IDInfo'; +import { TabContext } from '../context/TabContext'; +import { GlobalSnackbarContext } from '../context/GlobalSnackbarContext'; +import { Recording } from '../types/Recording'; +import { RecordingUpdate } from '../types/RecordingUpdate'; +import { RecordingListItem } from '../types/RecordingListItem'; const useStyles = makeStyles((theme) => ({ wrapper: { @@ -21,7 +23,7 @@ const useStyles = makeStyles((theme) => ({ column: { display: 'flex', flexFlow: 'column nowrap', - maxHeight: '98vh', + maxHeight: window.innerHeight - 56, height: '100%', padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`, width: '100%', @@ -35,72 +37,119 @@ const useStyles = makeStyles((theme) => ({ export default function Dashboard() { const [changeConnectionOpen, setChangeConnectionOpen] = useState(false); - const loginDetailsContext = useContext(LoginDetailsContext); - - const location = useLocation(); - const history = useHistory(); - - useEffect(() => { - if ( - location.pathname === routes.dashboard && - !changeConnectionOpen && - !loginDetailsContext?.loginDetails - ) { - history.push(routes.login); - } - }, []); + const { dispatch } = useContext(TabContext); + const globalSnackbarContext = useContext(GlobalSnackbarContext); function openConnectionDialog() { setChangeConnectionOpen(true); } + function handleNewRecording(recording: RecordingUpdate) { + const { + result, + error, + explainAnalyze, + query, + label, + elapsed, + color, + tabID, + } = recording; + const recordingID = uuid(); + const tempChartData = result?.memoryPerformance || []; + const chartDataLength = tempChartData?.length || 0; + const totalTime = + tempChartData[chartDataLength - 1]?.relative_time * 1000 || NaN; + const newRecording: Recording = { + queryOutput: result?.result || {}, + error, + stageTimes: result?.stageTimes, + chartData: result?.memoryPerformance, + optimizerTrace: result?.optimizerTrace, + chartColors: result?.chartColors, + explainAnalyze: + explainAnalyze || query.toLowerCase().includes('explain analyze'), + explainAnalyzeTree: result?.explainAnalyze, + label, + uuid: recordingID, + isSaved: false, + query, + elapsed: totalTime || elapsed || -1, + top3Memory: result?.top3Memory, + }; + let formattedLabel; + formattedLabel = error ? 'Error' : ''; + formattedLabel = label.length ? label : formattedLabel; + const newListItem: RecordingListItem = { + query, + elapsed: totalTime || elapsed || -1, + label: formattedLabel, + color, + uuid: recordingID, + viewing: true, + isSaved: false, + }; + dispatch({ + type: 'ADD_RECORDING', + payload: { + tabID: tabID || '', + recording: newRecording, + recordingListItem: newListItem, + recordingID, + }, + }); + globalSnackbarContext?.setPopupMessage( + `Recording finished in ${(elapsed || -1).toFixed(1)} ms` + ); + globalSnackbarContext?.setPopupSeverity('success'); + globalSnackbarContext?.setPopupOpen(true); + } + const classes = useStyles(); return ( <div className={classes.wrapper}> - <RecordingProvider> - <PanelGroup - borderColor="#4B4B4B" - spacing={16} - panelWidths={[ - { minSize: 200, size: 700, resize: 'dynamic' }, - { minSize: 200, resize: 'dynamic' }, - ]} - > - <div className={classes.column}> - <div className={classes.item}> - <Button - variant="outlined" - color="primary" - onClick={() => openConnectionDialog()} - > - Change connection - </Button> - <Dialog - open={changeConnectionOpen} - onClose={() => setChangeConnectionOpen(false)} - > - <Login onConnect={() => setChangeConnectionOpen(false)} /> - </Dialog> - </div> - <div className={classes.item}> - <IDInfo /> - </div> - <div className={classes.item}> - <QueryRecorder /> - </div> - <div className={classes.item}> - <ErrorView /> - </div> - <div className={classes.item}> - <Recordings /> - </div> + <PanelGroup + borderColor="#404040" + spacing={16} + panelWidths={[ + { minSize: 200, size: 700, resize: 'dynamic' }, + { minSize: 200, resize: 'dynamic' }, + ]} + > + <div className={classes.column}> + <div className={classes.item}> + <Button + variant="outlined" + color="primary" + onClick={() => openConnectionDialog()} + > + Change connection + </Button> + <Dialog + open={changeConnectionOpen} + onClose={() => setChangeConnectionOpen(false)} + > + <Login onConnect={() => setChangeConnectionOpen(false)} /> + </Dialog> + </div> + <div className={classes.item}> + <IDInfo /> + </div> + <div className={classes.item}> + <QueryRecorder onNewRecording={handleNewRecording} /> + </div> + <div className={classes.item}> + <ErrorView /> </div> - <div className={classes.column}> - <ResultView /> + <div className={classes.item}> + <Recordings /> </div> - </PanelGroup> - </RecordingProvider> + </div> + <div className={classes.column}> + <ResultView /> + </div> + </PanelGroup> </div> ); } diff --git a/app/containers/LoginScreen.tsx b/app/containers/LoginScreen.tsx index fe128ae2dec5206c4fd29c3ca7accc2d3a7e92b7..7e1a087e8363d106dadebef021d1f6cc44480426 100644 --- a/app/containers/LoginScreen.tsx +++ b/app/containers/LoginScreen.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState, useContext } from 'react'; import { Typography, makeStyles } from '@material-ui/core'; -import Login from '../components/Login'; +import Login from '../components/login/Login'; import FileIO from '../../backend/utils/FileIO'; import { LoginDetailsContext } from '../context/LoginDetailsContext'; diff --git a/app/containers/ResultView.tsx b/app/containers/ResultView.tsx index d82ef6212ff009785bec41fe9637ab70ffe88fbe..86712e8059557440ee20a0d36f6504fdf3c11b5b 100644 --- a/app/containers/ResultView.tsx +++ b/app/containers/ResultView.tsx @@ -7,14 +7,14 @@ import { Tooltip, } from '@material-ui/core'; import React, { useContext, useState } from 'react'; -import { FlameGraph } from 'react-flame-graph'; -import MemoryChart from '../components/MemoryChart'; -import OptimizerTrace from '../components/OptimizerTrace'; -import ResultTable from '../components/ResultTable'; -import { RecordingContext } from '../context/RecordingContext'; -import TreeViewExplainAnalyze from '../components/TreeViewExplainAnalyze'; -import ExplainAnalyzeTable from '../components/ExplainAnalyzeTable'; -import { ExplainAnalyzeNode } from '../../backend/data-processor/types/ExplainAnalyzeNode'; +import FlameGraphExplainAnalyze from '../components/results/FlameGraphExplainAnalyze'; +import MemoryPerformance from '../components/results/MemoryPerformance/MemoryPerformance'; +import OptimizerTrace from '../components/results/OptimizerTrace'; +import ResultTable from '../components/results/ResultTable'; +import ResultGrid from '../components/results/ResultGrid'; +import TreeViewExplainAnalyze from '../components/results/TreeViewExplainAnalyze'; +import { TabContext } from '../context/TabContext'; +import { MemoryPerformanceProvider } from '../context/MemoryPerformanceContext'; const useStyles = makeStyles((theme) => ({ wrapper: { @@ -33,38 +33,27 @@ const useStyles = makeStyles((theme) => ({ }, })); -const emptyNode: ExplainAnalyzeNode = { - id: '0', - offset: -1, - name: 'root', - time: '', - rows: -1, - value: 0, - timeFirstRow: 0, - timeAllRows: 0, - loops: -1, - children: [], -}; - export default function ResultView() { const [showMemoryChart, setShowMemoryChart] = useState(true); - const [showOptimizerTrace, setShowOptimizerTrace] = useState(false); + const [showOptimizerTrace, setShowOptimizerTrace] = useState(true); const [showResultTable, setShowResultTable] = useState(false); - const [showExplainAnalyze, setShowExplainAnalyze] = useState(false); - const [selectedNode, setSelectedNode] = useState(emptyNode); - const [hoveredNode, setHoveredNode] = useState(emptyNode); + const [showExplainAnalyze, setShowExplainAnalyze] = useState(true); - const recordingContext = useContext(RecordingContext); + const { state: tabState } = useContext(TabContext); + const { activeTabID, tabs } = tabState; + const tab = tabs[activeTabID]; + const recordings = tab?.recordings || {}; + const activeRecordingID = tab?.activeRecordingID || ''; const classes = useStyles(); return ( <div className={classes.wrapper} style={{ minWidth: '200px' }}> <div className={classes.item}> - <Tooltip title={recordingContext?.activeRecording?.label || ''}> + <Tooltip title={recordings[activeRecordingID]?.label || ''}> <Typography variant="h4" className={classes.title}> {`Results for recording ${ - recordingContext?.activeRecording?.label || '' + recordings[activeRecordingID]?.label || '' }`} </Typography> </Tooltip> @@ -100,7 +89,7 @@ export default function ResultView() { /> } /> - {recordingContext?.activeRecording?.explainAnalyze && ( + {recordings[activeRecordingID]?.explainAnalyze && ( <FormControlLabel label="Show explain analyze" control={ @@ -117,7 +106,7 @@ export default function ResultView() { </div> {showResultTable && ( <Paper className={classes.item}> - <ResultTable /> + <ResultGrid /> </Paper> )} {showMemoryChart && ( @@ -125,45 +114,31 @@ export default function ResultView() { className={classes.item} style={{ paddingBottom: 8, paddingTop: 8 }} > - <MemoryChart /> + {/* <MemoryChart /> */} + <MemoryPerformanceProvider> + <MemoryPerformance /> + </MemoryPerformanceProvider> </Paper> )} - {showOptimizerTrace && recordingContext?.activeRecording?.optimizerTrace && ( + {showOptimizerTrace && recordings[activeRecordingID]?.optimizerTrace && ( <Paper className={classes.item} style={{ padding: 8 }}> <OptimizerTrace /> </Paper> )} {showExplainAnalyze && - recordingContext?.activeRecording?.explainAnalyzeTree && ( + recordings[activeRecordingID]?.explainAnalyze && + recordings[activeRecordingID]?.explainAnalyzeTree && ( <Paper className={classes.item}> - <FlameGraph - data={recordingContext?.activeRecording?.explainAnalyzeTree} - height={200} - width={800} - disableDefaultTooltips - onChange={(node: { - source: React.SetStateAction<ExplainAnalyzeNode>; - }) => { - setSelectedNode(node.source); - }} - onMouseOver={( - event: any, - itemData: React.SetStateAction<ExplainAnalyzeNode> - ) => { - setHoveredNode(itemData); - }} - onMouseOut={(event: any, itemData: any) => { - setHoveredNode(selectedNode); - }} - /> - <ExplainAnalyzeTable node={hoveredNode} /> + <FlameGraphExplainAnalyze /> + </Paper> + )} + {showExplainAnalyze && + recordings[activeRecordingID]?.explainAnalyze && + recordings[activeRecordingID]?.explainAnalyzeTree && ( + <Paper className={classes.item}> + <TreeViewExplainAnalyze /> </Paper> )} - {showExplainAnalyze && ( - <Paper className={classes.item}> - <TreeViewExplainAnalyze /> - </Paper> - )} </div> ); } diff --git a/app/containers/TabContainer.tsx b/app/containers/TabContainer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cf0b926608c56e06fb12816287103ab31b86d94c --- /dev/null +++ b/app/containers/TabContainer.tsx @@ -0,0 +1,52 @@ +import React, { useContext, useEffect } from 'react'; +import { Switch, Route, useLocation, useHistory } from 'react-router-dom'; +import { LoginDetailsProvider } from '../context/LoginDetailsContext'; +import routes from '../routes'; +import LoginScreen from './LoginScreen'; +import Dashboard from './Dashboard'; +import TabBar from '../components/tabs/TabBar'; +import { TabContext } from '../context/TabContext'; +import { GlobalSnackbarProvider } from '../context/GlobalSnackbarContext'; +import GlobalSnackbar from '../components/dashboard/GlobalSnackbar'; + +export default function TabContainer() { + const { state: tabState, dispatch } = useContext(TabContext); + const { activeTabID, manager } = tabState; + + const location = useLocation(); + const history = useHistory(); + + function switchToTab(tabID: string, isNew = false) { + const connection = manager.connections[tabID]; + if ( + isNew || + (location.pathname === routes.dashboard && !connection?.client) + ) { + history.push(routes.login); + } else if (connection?.client && location.pathname !== routes.dashboard) { + history.push(routes.dashboard); + } + } + + // Need to ensure we are in a legal state at the start (no unconnected dashboard) + useEffect(() => { + switchToTab(activeTabID); + }, [activeTabID]); + + return ( + <GlobalSnackbarProvider> + <GlobalSnackbar /> + <LoginDetailsProvider> + <TabBar /> + <Switch> + <Route exact path={routes.login}> + <LoginScreen /> + </Route> + <Route path={routes.dashboard}> + <Dashboard /> + </Route> + </Switch> + </LoginDetailsProvider> + </GlobalSnackbarProvider> + ); +} diff --git a/app/context/GlobalSnackbarContext.tsx b/app/context/GlobalSnackbarContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..eebe28af4819b34d04815279c1ccb8113a60a990 --- /dev/null +++ b/app/context/GlobalSnackbarContext.tsx @@ -0,0 +1,41 @@ +import React, { useState, createContext } from 'react'; + +type ChildrenType = { + children: React.ReactNode; +}; + +type PopupSeverity = 'success' | 'info' | 'warning' | 'error' | undefined; + +type ContextType = { + popupMessage?: string; + popupSeverity: PopupSeverity; + popupOpen: boolean; + setPopupMessage: (message: string) => void; + setPopupSeverity: (severity: PopupSeverity) => void; + setPopupOpen: (open: boolean) => void; +}; + +export const GlobalSnackbarContext = createContext<ContextType | undefined>( + undefined +); + +export const GlobalSnackbarProvider = ({ children }: ChildrenType) => { + const [popupMessage, setPopupMessage] = useState<string>(); + const [popupSeverity, setPopupSeverity] = useState<PopupSeverity>(); + const [popupOpen, setPopupOpen] = useState<boolean>(false); + + return ( + <GlobalSnackbarContext.Provider + value={{ + popupMessage, + popupSeverity, + popupOpen, + setPopupMessage, + setPopupSeverity, + setPopupOpen, + }} + > + {children} + </GlobalSnackbarContext.Provider> + ); +}; diff --git a/app/context/LoginDetailsContext.tsx b/app/context/LoginDetailsContext.tsx index b54b0c6d67337d25bd87b3da88a487dc3e891589..b19671204c67a7760b26c8f6c30d244cf5fd8611 100644 --- a/app/context/LoginDetailsContext.tsx +++ b/app/context/LoginDetailsContext.tsx @@ -1,5 +1,5 @@ import React, { useState, createContext } from 'react'; -import { LoginDetails } from '../../backend/utils/LoginDetails'; +import { LoginDetails } from '../../backend/types/LoginDetails'; type ChildrenType = { children: React.ReactNode; diff --git a/app/context/MemoryPerformanceContext.tsx b/app/context/MemoryPerformanceContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..eeac8aba31e4d2b8b05ec714b6b94e0b49d02945 --- /dev/null +++ b/app/context/MemoryPerformanceContext.tsx @@ -0,0 +1,64 @@ +import React, { useState, createContext } from 'react'; + +type ChildrenType = { + children: React.ReactNode; +}; + +type ContextType = { + disabled: Array<string>; + setDisabled: (value: Array<string>) => void; + validStages: Array<string>; + setValidStages: (value: Array<string>) => void; + openEvent: boolean; + setOpenEvent: (value: boolean) => void; + openStage: boolean; + setOpenStage: (value: boolean) => void; + eventsChecked: Array<string>; + setEventsChecked: (value: Array<string>) => void; + stagesChecked: Array<string>; + setStagesChecked: (value: Array<string>) => void; + filteredEvents: Array<string>; + setFilteredEvents: (value: Array<string>) => void; + filteredStages: Array<string>; + setFilteredStages: (value: Array<string>) => void; +}; + +export const MemoryPerformanceContext = createContext<ContextType | undefined>( + undefined +); + +export const MemoryPerformanceProvider = ({ children }: ChildrenType) => { + const [disabled, setDisabled] = useState<Array<string>>([]); + const [validStages, setValidStages] = useState<Array<string>>([]); + const [openEvent, setOpenEvent] = useState(false); + const [openStage, setOpenStage] = useState(false); + const [eventsChecked, setEventsChecked] = useState<Array<string>>([]); + const [stagesChecked, setStagesChecked] = useState<Array<string>>([]); + const [filteredEvents, setFilteredEvents] = useState<Array<string>>([]); + const [filteredStages, setFilteredStages] = useState<Array<string>>([]); + + return ( + <MemoryPerformanceContext.Provider + value={{ + disabled, + setDisabled, + validStages, + setValidStages, + openEvent, + setOpenEvent, + openStage, + setOpenStage, + eventsChecked, + setEventsChecked, + stagesChecked, + setStagesChecked, + filteredEvents, + setFilteredEvents, + filteredStages, + setFilteredStages, + }} + > + {children} + </MemoryPerformanceContext.Provider> + ); +}; diff --git a/app/context/RecordingContext.tsx b/app/context/RecordingContext.tsx deleted file mode 100644 index 6eae3d1d8ae259d0009a81d3e6ca384f6aecd8a9..0000000000000000000000000000000000000000 --- a/app/context/RecordingContext.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useState, createContext } from 'react'; -import { Recording } from '../types/Recording'; -import { RecordingListItem } from '../types/RecordingListItem'; - -type ChildrenType = { - children: React.ReactNode; -}; - -type ContextType = { - activeRecording?: Recording; - setActiveRecording: (value?: Recording) => void; - recordings?: Recording[]; - setRecordings: (value?: Recording[]) => void; - recordingListItems?: RecordingListItem[]; - setRecordingListItems: (value?: RecordingListItem[]) => void; -}; - -export const RecordingContext = createContext<ContextType | undefined>( - undefined -); - -export const RecordingProvider = ({ children }: ChildrenType) => { - const [activeRecording, setActiveRecording] = useState<Recording | undefined>( - undefined - ); - const [recordings, setRecordings] = useState<Recording[] | undefined>([]); - const [recordingListItems, setRecordingListItems] = useState< - RecordingListItem[] | undefined - >([]); - - return ( - <RecordingContext.Provider - value={{ - activeRecording, - setActiveRecording, - recordings, - setRecordings, - recordingListItems, - setRecordingListItems, - }} - > - {children} - </RecordingContext.Provider> - ); -}; diff --git a/app/context/TabContext.tsx b/app/context/TabContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d84a62a1661140501299966082f2b228b88704fa --- /dev/null +++ b/app/context/TabContext.tsx @@ -0,0 +1,437 @@ +/* eslint-disable no-case-declarations */ +import { keys, valuesIn } from 'lodash'; +import React, { createContext, useReducer } from 'react'; +import { v4 as uuid } from 'uuid'; +import SqlManager from '../../backend/recorder/SqlManager'; +import SqlManagerSingleton from '../../backend/recorder/SqlManagerSingleton'; +import { DisplayTab } from '../types/DisplayTab'; +import { Recording } from '../types/Recording'; +import { RecordingListItem } from '../types/RecordingListItem'; +import { Tab } from '../types/Tab'; + +interface ChildrenType { + children: React.ReactNode; +} + +export interface RecordingPayload { + tabID: string; + recordingID?: string; + recording?: Recording; + recordingListItem?: RecordingListItem; + value?: string | number | boolean; +} + +export interface DisplayTabLabelPayload { + id: string; + label: string; +} + +export interface TabState { + activeTabID: string; + createdTabs: number; + tabs: { [key: string]: Tab }; + displayTabs: { [key: string]: DisplayTab }; + manager: SqlManager; +} + +export interface TabAction { + type: + | 'ADD_RECORDING' + | 'DELETE_RECORDING' + | 'SET_RECORDING_LABEL' + | 'SET_RECORDING_COLOR' + | 'SET_ACTIVE_RECORDING' + | 'SET_RECORDING_STATE' + | 'SET_INPUT_QUERY' + | 'SET_SHOW_ADVANCED_OPTIONS' + | 'SET_EXPLAIN_ANALYZE' + | 'SET_TIMESTEP' + | 'SET_RECORDING_INPUT_LABEL' + | 'SET_RECORDING_INPUT_COLOR' + | 'SET_QUERY_RUNNING' + | 'SET_RECORDING_RUNNING' + | 'SET_DISPLAYTAB_LABEL' + | 'ADD_TAB' + | 'SET_ACTIVE_TAB' + | 'DELETE_TAB'; + payload?: + | Tab + | DisplayTab + | string + | boolean + | number + | DisplayTabLabelPayload + | RecordingPayload; +} + +const firstTabID = uuid(); + +const initialState: TabState = { + activeTabID: firstTabID, + createdTabs: 1, + tabs: { + [firstTabID]: { + tabID: firstTabID, + serial: 1, + recordings: {}, + recordingListItems: {}, + recordingRunning: false, + queryRunning: false, + inputQuery: '', + inputLabel: '', + inputColor: '', + explainAnalyze: false, + timestep: 0.2, + showAdvancedOptions: false, + recordingState: '', + }, + }, + displayTabs: { + [firstTabID]: { tabID: firstTabID, tabLabel: 'New connection' }, + }, + manager: SqlManagerSingleton.getInstance(), +}; + +const tabReducer = (tabState: TabState, action: TabAction): TabState => { + switch (action.type) { + case 'ADD_TAB': + const tabID = uuid(); + const tabLabel = 'New connection'; + const changedDisplayTabs = { ...tabState.displayTabs }; + changedDisplayTabs[tabID] = { + tabID, + tabLabel, + }; + const changedTabs = { ...tabState.tabs }; + changedTabs[tabID] = { + tabID, + serial: tabState.createdTabs + 1, + recordings: {}, + recordingListItems: {}, + recordingRunning: false, + queryRunning: false, + inputQuery: '', + inputLabel: '', + inputColor: '', + explainAnalyze: false, + timestep: 0.2, + showAdvancedOptions: false, + recordingState: '', + }; + return { + ...tabState, + tabs: changedTabs, + displayTabs: changedDisplayTabs, + activeTabID: tabID, + }; + case 'DELETE_TAB': + if (typeof action.payload === 'string') { + const id = action.payload; + const tabs = { ...tabState.tabs }; + const displayTabs = { ...tabState.displayTabs }; + delete tabs[id]; + delete displayTabs[id]; + const ids = keys(displayTabs); + let newIndex = ids.length - 1; // First assuming the tab was the rightmost one + if (ids.length < 1) return tabState; // We do not delete the last tab (for now) + if (newIndex < 0) newIndex = 0; // Avoid index out of bounds + const activeTabID = ids[newIndex]; + tabState.manager.removeConnection(id); + return { ...tabState, activeTabID, tabs, displayTabs }; + } + return tabState; + case 'SET_ACTIVE_TAB': + if (typeof action.payload === 'string') { + const activeTabID = action.payload; + return { ...tabState, activeTabID }; + } + return tabState; + case 'SET_DISPLAYTAB_LABEL': + if ( + (action.payload as DisplayTabLabelPayload)?.id && + (action.payload as DisplayTabLabelPayload)?.label !== undefined + ) { + const { id, label } = action.payload as DisplayTabLabelPayload; + const displayTabs = { ...tabState.displayTabs }; + displayTabs[id].tabLabel = label; + return { ...tabState, displayTabs }; + } + return tabState; + case 'ADD_RECORDING': + if ( + (action.payload as RecordingPayload).recording && + (action.payload as RecordingPayload).recordingListItem && + (action.payload as RecordingPayload).recordingID && + (action.payload as RecordingPayload).tabID + ) { + const { + tabID: id, + recording, + recordingListItem, + recordingID, + } = action.payload as RecordingPayload; + if (!recording || !recordingListItem || !recordingID) return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + const recordings = { ...tab.recordings }; + const recordingListItems = { ...tab.recordingListItems }; + recordings[recordingID] = recording; + recordingListItems[recordingID] = recordingListItem; + tab.recordings = recordings; + tab.recordingListItems = recordingListItems; + tab.activeRecordingID = recordingID; + tab.inputLabel = ''; + tab.recordingRunning = false; + tab.queryRunning = false; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_ACTIVE_RECORDING': + if ( + (action.payload as RecordingPayload).recordingID && + (action.payload as RecordingPayload).tabID + ) { + const { tabID: id, recordingID } = action.payload as RecordingPayload; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.activeRecordingID = recordingID; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_RECORDING_STATE': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'string') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.recordingState = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_INPUT_QUERY': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'string') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.inputQuery = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_SHOW_ADVANCED_OPTIONS': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'boolean') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.showAdvancedOptions = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_EXPLAIN_ANALYZE': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'boolean') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.explainAnalyze = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_TIMESTEP': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'number') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.timestep = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_RECORDING_INPUT_LABEL': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'string') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.inputLabel = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_RECORDING_INPUT_COLOR': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'string') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.inputColor = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_QUERY_RUNNING': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'boolean') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.queryRunning = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_RECORDING_RUNNING': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { tabID: id, value } = action.payload as RecordingPayload; + if (typeof value !== 'boolean') return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + tab.recordingRunning = value; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_RECORDING_LABEL': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).recordingID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { + tabID: id, + value, + recordingID, + } = action.payload as RecordingPayload; + if (typeof value !== 'string' || !recordingID) return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + const recordings = { ...tab.recordings }; + const recordingListItems = { ...tab.recordingListItems }; + recordings[recordingID].label = value; + recordingListItems[recordingID].label = value; + tab.recordings = recordings; + tab.recordingListItems = recordingListItems; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'SET_RECORDING_COLOR': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).recordingID && + (action.payload as RecordingPayload).value !== undefined + ) { + const { + tabID: id, + value, + recordingID, + } = action.payload as RecordingPayload; + if (typeof value !== 'string' || !recordingID) return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + const recordingListItems = { ...tab.recordingListItems }; + recordingListItems[recordingID].color = value; + tab.recordingListItems = recordingListItems; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + case 'DELETE_RECORDING': + if ( + (action.payload as RecordingPayload).tabID && + (action.payload as RecordingPayload).recordingID + ) { + const { tabID: id, recordingID } = action.payload as RecordingPayload; + if (!recordingID) return tabState; + const tabs = { ...tabState.tabs }; + const tab = tabs[id]; + const recordings = { ...tab.recordings }; + const recordingListItems = { ...tab.recordingListItems }; + delete recordings[recordingID]; + delete recordingListItems[recordingID]; + const listItemsArray = valuesIn(tab.recordingListItems); + const index = listItemsArray.findIndex( + (item) => item.uuid === recordingID + ); + let activeRecordingID; + if (recordingID === tab?.activeRecordingID) { + let newIndex = index - 1; + if (newIndex < 0) { + newIndex = 0; + } + if (listItemsArray.length < 1) { + newIndex = -1; + } else if (listItemsArray.length === 1) { + newIndex = 0; + } + if (newIndex !== -1 && listItemsArray[newIndex]) { + activeRecordingID = + newIndex !== -1 ? listItemsArray[newIndex].uuid : undefined; + } + } + tab.recordingListItems = recordingListItems; + tab.recordings = recordings; + tab.activeRecordingID = activeRecordingID; + tabs[id] = tab; + return { ...tabState, tabs }; + } + return tabState; + default: + return tabState; + } +}; + +export const TabContext = createContext<{ + state: TabState; + dispatch: React.Dispatch<TabAction>; +}>({ state: initialState, dispatch: () => null }); + +export const TabProvider = ({ children }: ChildrenType) => { + const [state, dispatch] = useReducer(tabReducer, initialState); + + return ( + <TabContext.Provider + value={{ + state, + dispatch, + }} + > + {children} + </TabContext.Provider> + ); +}; diff --git a/app/main.dev.ts b/app/main.dev.ts index 28d4d12c5feee2d2d05a9bce195e6fc1c4dbf38c..153e7254ae57904c97d81f0a7ce5473ece390025 100644 --- a/app/main.dev.ts +++ b/app/main.dev.ts @@ -14,13 +14,8 @@ import 'regenerator-runtime/runtime'; import { app, BrowserWindow } from 'electron'; import { autoUpdater } from 'electron-updater'; import log from 'electron-log'; -// import installExtension, { -// REACT_DEVELOPER_TOOLS, -// } from 'electron-devtools-installer'; import MenuBuilder from './menu'; -// app.allowRendererProcessReuse = true; - export default class AppUpdater { constructor() { log.transports.file.level = 'info'; @@ -36,36 +31,14 @@ if (process.env.NODE_ENV === 'production') { sourceMapSupport.install(); } -// if ( -// process.env.NODE_ENV === 'development' || -// process.env.DEBUG_PROD === 'true' -// ) { -// require('electron-debug')(); -// } - -// const installExtensions = async () => { -// const forceDownload = !!process.env.UPGRADE_EXTENSIONS; -// const extensions = [REACT_DEVELOPER_TOOLS]; - -// return Promise.all( -// extensions.map((name) => installExtension(name, forceDownload)) -// ).catch(console.log); -// }; - const createWindow = async () => { - // if ( - // process.env.NODE_ENV === 'development' || - // process.env.DEBUG_PROD === 'true' - // ) { - // await installExtensions(); - // } - mainWindow = new BrowserWindow({ show: false, width: 1080, height: 900, webPreferences: { nodeIntegration: true, + nativeWindowOpen: true, }, }); diff --git a/app/package.json b/app/package.json index e5abeef9c5f0262bb9b92dcd07ef46e74369ce60..83d9efbe70c8f20463713e08519086803a245a70 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "mysql-query-profiler", "productName": "MySQL Query Profiler", - "version": "0.3.1", + "version": "0.4.0", "description": "A profiler for MySQL queries using Electron and React", "main": "./main.prod.js", "author": { diff --git a/app/smartToolTip.js b/app/smartToolTip.js new file mode 100644 index 0000000000000000000000000000000000000000..8c474911ecac73585d6bed480d3d3f9e5eafce4a --- /dev/null +++ b/app/smartToolTip.js @@ -0,0 +1,58 @@ +/* eslint-disable linebreak-style */ +/** @flow */ + +import { useLayoutEffect, useRef } from 'react'; + +const TOOLTIP_OFFSET = 4; + +export default function useSmartTooltip({ mouseX, mouseY }) { + // $FlowFixMe Flow generics syntax causes a compilation error with the current Babel version + const ref = useRef(null); + useLayoutEffect(() => { + const element = ref.current; + if (element != null) { + // Let's check the vertical position. + if ( + mouseY + TOOLTIP_OFFSET + element.offsetHeight >= + window.innerHeight + ) { + // The tooltip doesn't fit below the mouse cursor (which is our + // default strategy). Therefore we try to position it either above the + // mouse cursor or finally aligned with the window's top edge. + if (mouseY - TOOLTIP_OFFSET - element.offsetHeight > 0) { + // We position the tooltip above the mouse cursor if it fits there. + element.style.top = `${ + mouseY - element.offsetHeight - TOOLTIP_OFFSET + }px`; + } else { + // Otherwise we align the tooltip with the window's top edge. + element.style.top = '0px'; + } + } else { + element.style.top = `${mouseY + TOOLTIP_OFFSET}px`; + } + + // Now let's check the horizontal position. + if (mouseX + TOOLTIP_OFFSET + element.offsetWidth >= window.innerWidth) { + // The tooltip doesn't fit at the right of the mouse cursor (which is + // our default strategy). Therefore we try to position it either at the + // left of the mouse cursor or finally aligned with the window's left + // edge. + if (mouseX - TOOLTIP_OFFSET - element.offsetWidth > 0) { + // We position the tooltip at the left of the mouse cursor if it fits + // there. + element.style.left = `${ + mouseX - element.offsetWidth - TOOLTIP_OFFSET + }px`; + } else { + // Otherwise, align the tooltip with the window's left edge. + element.style.left = '0px'; + } + } else { + element.style.left = `${mouseX + TOOLTIP_OFFSET}px`; + } + } + }); + + return ref; +} diff --git a/app/types/DisplayTab.ts b/app/types/DisplayTab.ts new file mode 100644 index 0000000000000000000000000000000000000000..49849ef22610cbac5dde6417888b869a9100dac0 --- /dev/null +++ b/app/types/DisplayTab.ts @@ -0,0 +1,4 @@ +export interface DisplayTab { + tabID: string; // UUID + tabLabel: string; +} diff --git a/app/types/Recording.ts b/app/types/Recording.ts index 890aa775846904ef63efebfaf758663321231539..c032fe1b2712ffb198bf0a04b80986657246fb90 100644 --- a/app/types/Recording.ts +++ b/app/types/Recording.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { ResultValue } from '@mysql/xdevapi'; import { ChartColors } from '../../backend/data-processor/types/ChartColors'; import { ChartData } from '../../backend/data-processor/types/ChartData'; @@ -9,11 +10,15 @@ export interface Recording { chartData?: ChartData; stageTimes?: StageTimes; chartColors?: ChartColors; + top3Memory?: string[]; optimizerTrace?: ResultValue[][]; queryOutput?: QueryOutput; explainAnalyze: boolean; explainAnalyzeTree?: ExplainAnalyzeNode; - error?: string; + error?: any; label: string; // On both in order to avoid having to create an 'activeRecordingListItem' uuid: string; + isSaved: boolean; + query: string; + elapsed: number; } diff --git a/app/types/RecordingListItem.ts b/app/types/RecordingListItem.ts index fc61436c2d49e3a2665e798e3f41d3fb30c318eb..3060524611c3a6498d534ce104bc249335780153 100644 --- a/app/types/RecordingListItem.ts +++ b/app/types/RecordingListItem.ts @@ -5,4 +5,5 @@ export interface RecordingListItem { label: string; color: string; viewing: boolean; + isSaved: boolean; } diff --git a/app/types/RecordingUpdate.ts b/app/types/RecordingUpdate.ts new file mode 100644 index 0000000000000000000000000000000000000000..20ccd6b57db006bdf2b1d0dd3d060fd244ae0056 --- /dev/null +++ b/app/types/RecordingUpdate.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { RawRecording } from '../../backend/recorder/SqlManager'; + +export interface RecordingUpdate { + result?: RawRecording; + elapsed?: number; + label: string; + color: string; + error?: any; + explainAnalyze: boolean; + query: string; + tabID?: string; +} diff --git a/app/types/Tab.ts b/app/types/Tab.ts new file mode 100644 index 0000000000000000000000000000000000000000..b0272ccae79f0b08fcb8f358932d955db471325e --- /dev/null +++ b/app/types/Tab.ts @@ -0,0 +1,19 @@ +import { Recording } from './Recording'; +import { RecordingListItem } from './RecordingListItem'; + +export interface Tab { + tabID: string; // UUID + serial: number; // How many tabs had been made when this one was made + activeRecordingID?: string; + recordings: { [key: string]: Recording }; + recordingListItems: { [key: string]: RecordingListItem }; + recordingRunning: boolean; + queryRunning: boolean; + inputQuery: string; + timestep: number; + explainAnalyze: boolean; + inputLabel: string; + inputColor: string; + showAdvancedOptions: boolean; + recordingState: string; // What the manager is doing while recording +} diff --git a/app/types/TabRecordingContext.ts b/app/types/TabRecordingContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..01149796c48d952849416b0e0d6fb0e53b412d61 --- /dev/null +++ b/app/types/TabRecordingContext.ts @@ -0,0 +1,9 @@ +import { Recording } from './Recording'; +import { RecordingListItem } from './RecordingListItem'; + +export interface TabRecordingContext { + tabID: string; + activeRecordingID?: string; + recordings: { [key: string]: Recording }; + recordingListItems: { [key: string]: RecordingListItem }; +} diff --git a/backend/data-processor/DataProcessor.ts b/backend/data-processor/DataProcessor.ts index c3b0b6be4509db3c0499611fd87d0ffc7fbe4e84..edea9117090672abee4fe87abb7c29cb21f1b55b 100644 --- a/backend/data-processor/DataProcessor.ts +++ b/backend/data-processor/DataProcessor.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-plusplus */ /* eslint-disable class-methods-use-this */ import { ResultValue } from '@mysql/xdevapi'; import lodash from 'lodash'; @@ -59,7 +60,8 @@ export default class DataProcessor { timeLine.isBytesUsedZero = timeLine.bytesUsed.reduce((a, b) => a + b, 0) === 0; }); - return DataProcessor.getChartData(result); + // return DataProcessor.getChartData(result); + return result; } throw new Error('DataProcessor: performanceData had wrong format'); } @@ -101,6 +103,45 @@ export default class DataProcessor { throw new Error('DataProcessor: stageTimesRaw had wrong format'); } + static formatDate(d: Date) { + let month = `${d.getMonth() + 1}`; + let day = `${d.getDate()}`; + const year = d.getFullYear(); + + if (month.length < 2) month = `0${month}`; + if (day.length < 2) day = `0${day}`; + + return [year, month, day].join('-'); + } + + static calculateRowWidths({ + values, + labels, + types, + }: { + values: ResultValue[][]; + labels: string[]; + types: number[]; + }): number[] { + console.log(values); + console.log(labels); + const highest = labels.map((label) => label.length); + for (let i = 0; i < values.length; i++) { + const currentObject = values[0]; + for (let j = 0; j < currentObject.length; j++) { + const datatype = types[j]; + highest[j] = Math.max( + highest[j], + datatype === 12 + ? DataProcessor.formatDate(new Date(Number(currentObject[j]))) + .length + : String(currentObject[j]).length + ); + } + } + return highest.map((stringlength) => stringlength * 15); + } + static processOptimizerTrace(optimizerTrace?: Result) { let traceResult; if (optimizerTrace && optimizerTrace.values) { @@ -130,12 +171,23 @@ export default class DataProcessor { return chartColors; } + static processTop3ChartData(memoryPerformance: MemoryPerformance) { + const sortedChartData = lodash.sortBy(memoryPerformance, (o) => + lodash.sum(o.bytesUsed) + ); + const top3ChartData = lodash.takeRight(sortedChartData, 3); + const top3List: string[] = []; + lodash.forEach(top3ChartData, (o) => top3List.push(o.eventName)); + return top3List; + } + // -> Limit: 10 row(s) (actual time=0.098..0.103 rows=10 loops=1) // -> Table scan on employees (cost=30192.25 rows=299600) (actual time=0.095..0.099 rows=10 loops=1) // " Table scan on ints (actual time=0.006..0.029 rows=5 loops=1)" // " Materialize (actual time=2.662..2.697 rows=5 loops=1) " // " Rows fetched before execution (actual time=0.00 + static processExplainAnalyze(explainAnalyze?: Result) { if ( !explainAnalyze || @@ -155,6 +207,7 @@ export default class DataProcessor { timeFirstRow: 0, timeAllRows: 0, loops: -1, + additionalData: [], children: [], }; const regexp = /([ ]*)?-> (.+?)( \(cost=([\d.]+) rows=(\d+)\))? \(actual time=([\d.]+) rows=(\d+) loops=(\d+)\)/gm; @@ -166,9 +219,6 @@ export default class DataProcessor { matches.forEach((element) => { const timeResult = element[6].match(regexpTime); if (timeResult != null) { - const timeFirst = Number(timeResult[1]) * Number(element[8]); - const timeAll = Number(timeResult[2]) * Number(element[8]); - const node: ExplainAnalyzeNode = { id: idIterator.toString(), offset: element[1] === undefined ? 0 : element[1].length, @@ -176,13 +226,16 @@ export default class DataProcessor { cost_est: Number(element[4]), rows_est: Number(element[5]), time: element[6], - value: timeAll, - timeFirstRow: timeFirst, - timeAllRows: timeAll, + // Value = Total time = Time all rows * loops + value: Number(timeResult[2]) * Number(element[8]), + timeFirstRow: Number(timeResult[1]), + timeAllRows: Number(timeResult[2]), rows: Number(element[7]), loops: Number(element[8]), + additionalData: [], children: [], }; + console.log(node.time, node.timeAllRows, node.loops, node.value); nodes.push(node); idIterator += 1; } @@ -190,25 +243,25 @@ export default class DataProcessor { // Assign children of each object // Iterate through all objects - for (let i = 1; i < nodes.length; i += 1) { - // Set current node as child if its offset is smaller - if (nodes[i - 1].offset < nodes[i].offset) { - nodes[i - 1].children.push(nodes[i]); + nodes.forEach((node, index) => { + if (index === 0) return; + if (nodes[index - 1].offset < node.offset) { + nodes[index - 1].children.push(node); } else { // If current node's offset is smaller then it cannot be child of the previous node // Iterate backwards from current node until a node with smaller offset has been reached - for (let j = i - 1; i > 0; j -= 1) { - if (nodes[j].offset < nodes[i].offset) { - nodes[j].children.push(nodes[i]); + for (let j = index - 1; index > 0; j -= 1) { + if (nodes[j].offset < nodes[index].offset) { + nodes[j].children.push(nodes[index]); break; } } } - } + }); const node = DataProcessor.fixNodeTimes(nodes[0]); - DataProcessor.log(explainText); + // DataProcessor.log(explainText); return node; } @@ -218,6 +271,7 @@ export default class DataProcessor { * @param node */ static fixNodeTimes(node: ExplainAnalyzeNode) { + // const threshold = 0.05; if (!node.children.length) { return node; } @@ -226,8 +280,25 @@ export default class DataProcessor { const nextNode = DataProcessor.fixNodeTimes(element); sumChildren += nextNode.value; }); + // If parent nodes's total time (value) is a certain degree (threshold) smaller than sum of childrens' times, + // then parent's total time is set to THE SAME as childrens' total time + // If it's smaller than that the childrens' total time is ADDED to parent's total time + /* if (node.value < sumChildren * (1 - threshold)) { + const prev = String(node.value); + node.value += sumChildren; + console.log( + `${node.name} total time changed from ${prev} to ${String( + node.value + )} (added to old value)` + ); + } else */ if (node.value < sumChildren) { + const prev = node.value; node.value += sumChildren; + node.additionalData.push({ + description: 'Total time change', + data: `Total time changed from ${prev} to ${String(node.value)}`, + }); } return node; } diff --git a/backend/data-processor/types/ExplainAnalyzeNode.ts b/backend/data-processor/types/ExplainAnalyzeNode.ts index 2e5706165df24e59e6e4021758cb2d11b3b0d14c..7dced18ad9dbbf75c82ee426626036af1454ec38 100644 --- a/backend/data-processor/types/ExplainAnalyzeNode.ts +++ b/backend/data-processor/types/ExplainAnalyzeNode.ts @@ -1,7 +1,8 @@ +import { TableElement } from './TableElement'; + export interface ExplainAnalyzeNode { id: string; offset: number; - // Endret fra command til name, siden koponenten krever det name: string; cost_est?: number; rows_est?: number; @@ -11,5 +12,7 @@ export interface ExplainAnalyzeNode { timeAllRows: number; rows: number; loops: number; + // Each object in additionalData is added to the table associated to each node + additionalData: TableElement[]; children: ExplainAnalyzeNode[]; } diff --git a/backend/data-processor/types/TableElement.ts b/backend/data-processor/types/TableElement.ts new file mode 100644 index 0000000000000000000000000000000000000000..8dcb78e25923e225591028a879fd0900833a4e59 --- /dev/null +++ b/backend/data-processor/types/TableElement.ts @@ -0,0 +1,4 @@ +export interface TableElement { + description: string; + data: string; +} diff --git a/backend/recorder/SqlAgent.ts b/backend/recorder/SqlAgent.ts index 5bc7eed2007d9833766d8a0ca536cdc3ad4b5290..b1a418eb685d5d13430e65e9f19acea784123e6f 100644 --- a/backend/recorder/SqlAgent.ts +++ b/backend/recorder/SqlAgent.ts @@ -70,11 +70,13 @@ export default class SqlAgent { await sqlMonitor.connect(client, database); } - async killQuery(connectionID: string) { + async killQuery(connectionID?: string) { + if (!connectionID) return { results: undefined, error: undefined }; return this.executeQuery(`KILL QUERY ${connectionID}`); } - async killConnection(connectionID: string) { + async killConnection(connectionID?: string) { + if (!connectionID) return { results: undefined, error: undefined }; return this.executeQuery(`KILL CONNECTION ${connectionID};`); } diff --git a/backend/recorder/SqlManager.ts b/backend/recorder/SqlManager.ts index 1a54aaeaabe8cc08f9af34d76f891cb42a4e2618..dd40215fb67bf9b570644dfb4e94b30f7e816e4d 100644 --- a/backend/recorder/SqlManager.ts +++ b/backend/recorder/SqlManager.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { URI, Client, ResultValue } from '@mysql/xdevapi'; +import { Client, ResultValue } from '@mysql/xdevapi'; import FileIO from '../utils/FileIO'; import SqlRunner from './SqlRunner'; import SqlMonitor from './SqlMonitor'; @@ -10,86 +10,143 @@ import DataProcessor from '../data-processor/DataProcessor'; import SqlAgent from './SqlAgent'; import { ChartColors } from '../data-processor/types/ChartColors'; import { ExplainAnalyzeNode } from '../data-processor/types/ExplainAnalyzeNode'; +import { LoginDetails } from '../types/LoginDetails'; export interface RawRecording { - result?: { results?: Result; error?: any }; + result?: { results?: Result; error?: any; columnWidths?: number[] }; memoryPerformance?: ChartData; chartColors?: ChartColors; + top3Memory?: string[]; error?: any; optimizerTrace?: ResultValue[][]; stageTimes?: StageTimes; explainAnalyze?: ExplainAnalyzeNode; + tabID: string; } export default class SqlManager { - connection?: URI; - - database?: string; - - client?: Client; - - runner?: SqlRunner; - - monitor?: SqlMonitor; - - agent?: SqlAgent; - - userQueryIsRunning = false; - - cancel = false; + connections: { + [key: string]: { + loginDetails: LoginDetails; + client?: Client; + runner: SqlRunner; + monitor: SqlMonitor; + agent: SqlAgent; + running: boolean; + cancelled: boolean; + }; + } = {}; - async connect(connection?: URI, database?: string, savePassword?: boolean) { - this.connection = connection; - this.database = database; - this.runner = new SqlRunner(); - this.client = await this.runner.connect(connection, database); - this.monitor = new SqlMonitor(); - await this.monitor.connect(this.client, database); - this.agent = new SqlAgent(); - await this.agent.connect(this.client, database); - await FileIO.saveLoginDetails({ ...connection, database, savePassword }); + async connect(tabID: string, serial?: number, loginDetails?: LoginDetails) { + if (!loginDetails) return; + SqlManager.log(JSON.stringify(loginDetails, undefined, 2)); + if (!this.connections[tabID]) { + this.connections[tabID] = { + loginDetails, + runner: new SqlRunner(), + monitor: new SqlMonitor(), + agent: new SqlAgent(), + running: false, + cancelled: false, + }; + } + const connection = this.connections[tabID]; + connection.client = await connection.runner.connect(loginDetails); + await connection.monitor.connect( + connection.client, + loginDetails?.database, + serial + ); + await connection.agent.connect(connection.client, loginDetails?.database); + await FileIO.saveLoginDetails({ + ...loginDetails, + password: loginDetails.savePassword ? loginDetails.password : '', + }); } async record( query: string, + tabID: string, setUserQueryIsRunning: (running: boolean) => void, + stateCallback: (state: string) => void, monitorTimeStep: number, - explainAnalyze?: boolean, - stateCallback?: (state: string) => void + explainAnalyze?: boolean ): Promise<RawRecording> { + const connection = this.connections[tabID]; + const { runner, monitor, agent } = connection; + console.log( + `timestep: ${monitorTimeStep}, + query: ${query}, + tabID: ${tabID}, + runnerCID: ${runner.connectionID}, + runnerTID: ${runner.threadID}, + monitorCID: ${monitor.connectionID}, + monitorTID: ${monitor.threadID}` + ); if ( - !this.runner || - !this.monitor || - !this.runner.connectionID || - !this.runner.threadID || - !this.monitor.connectionID || - !this.monitor.threadID || - !this.agent + !runner.connectionID || + !runner.threadID || + !monitor.connectionID || + !monitor.threadID || + !monitorTimeStep || + query === undefined || + !tabID || + !setUserQueryIsRunning ) { throw new Error( 'SqlManager.record: Runner, monitor, agent or connectionID was missing. Maybe you forgot to call connect()?' ); } - const stageIgnoreBeforeResult = await this.monitor.getStageIgnoreBeforeResult( - this.runner.threadID + const stageIgnoreBeforeResult = await monitor.getStageIgnoreBeforeResult( + runner.threadID ); - const monitoring = this.monitor.monitorConnection( - this.runner.connectionID, + const monitoring = monitor.monitorConnection( + runner.connectionID, stageIgnoreBeforeResult, monitorTimeStep ); - stateCallback?.call(undefined, 'Waiting for monitor to be ready'); - await this.agent.waitRunning(this.monitor.threadID); + stateCallback('Waiting for monitor to be ready'); + await agent.waitRunning(monitor.threadID); - stateCallback?.call(undefined, 'Running query'); + stateCallback('Running query'); setUserQueryIsRunning(true); const t0 = performance.now(); - const result = await this.runner.executeQuery(query, explainAnalyze); + const result = await runner.executeQuery(query, explainAnalyze); const t1 = performance.now(); setUserQueryIsRunning(false); + if (connection.cancelled) { + connection.cancelled = false; + stateCallback(''); + return { error: 'cancelled', tabID }; + } - stateCallback?.call(undefined, 'Query done'); + // Handling error, aborting + if (result?.error) { + const optimizerTrace = await runner.getOptimizerTrace(); + stateCallback('Error detected, aborting monitoring'); + await this.reset(tabID, monitor.serial); + stateCallback(''); + return { + result: { + results: result.results, + error: result.error, + columnWidths: result.results + ? DataProcessor.calculateRowWidths(result.results) + : undefined, + }, + error: result.error, + explainAnalyze: result.results + ? DataProcessor.processExplainAnalyze(result.results) + : undefined, + optimizerTrace: optimizerTrace.results + ? DataProcessor.processOptimizerTrace(optimizerTrace.results) + : undefined, + tabID, + }; + } + + stateCallback('Query done'); const queryTime = t1 - t0; SqlManager.log( `Query took ${queryTime} ms, required at least ${ @@ -97,103 +154,122 @@ export default class SqlManager { } ms` ); - if (this.cancel) { - stateCallback?.call(undefined, 'Cancel detected, reconnecting'); - await this.connect(this.connection, this.database); - this.cancel = false; - return { error: 'cancelled' }; - } + if (queryTime < monitorTimeStep * 1000 * 2) { + stateCallback('Too short query, aborting monitoring'); + const optimizerTrace = await runner.getOptimizerTrace(); + await this.reset(tabID, monitor.serial); - // Handling error, aborting - if (result?.error) { - stateCallback?.call(undefined, 'Error detected, aborting monitoring'); - await this.reset(); - stateCallback?.call(undefined, ''); + stateCallback(''); return { - result, - error: result.error, + result: { + results: result.results, + error: result.error, + columnWidths: result.results + ? DataProcessor.calculateRowWidths(result.results) + : undefined, + }, explainAnalyze: result.results ? DataProcessor.processExplainAnalyze(result.results) : undefined, + optimizerTrace: optimizerTrace.results + ? DataProcessor.processOptimizerTrace(optimizerTrace.results) + : undefined, + tabID, }; } - stateCallback?.call(undefined, 'Getting optimizer trace'); - const optimizerTrace = await this.runner.getOptimizerTrace(); - stateCallback?.call( - undefined, + stateCallback('Getting optimizer trace'); + const optimizerTrace = await runner.getOptimizerTrace(); + + stateCallback( 'Waiting for monitoring to finish (may take up to 5 minutes)' ); await monitoring; - stateCallback?.call(undefined, 'Getting memory performance'); - const memoryPerformance = await this.monitor.dumpData(); - stateCallback?.call(undefined, 'Getting stage times'); - const stageTimes = await this.monitor.getStageTimes(); - stateCallback?.call(undefined, ''); + stateCallback('Getting memory performance'); + const memoryPerformance = await monitor.dumpData(); + stateCallback('Getting stage times'); + const stageTimes = await monitor.getStageTimes(); + stateCallback(''); + const mPerformance = DataProcessor.processMemoryPerformance( + memoryPerformance.results + ); + const chartData = DataProcessor.getChartData(mPerformance); return { - result, + result: { + results: result.results, + error: result.error, + columnWidths: result.results + ? DataProcessor.calculateRowWidths(result.results) + : undefined, + }, + memoryPerformance: chartData, + chartColors: DataProcessor.processChartColors(chartData), + top3Memory: DataProcessor.processTop3ChartData(mPerformance), + error: undefined, optimizerTrace: DataProcessor.processOptimizerTrace( optimizerTrace.results ), - memoryPerformance: DataProcessor.processMemoryPerformance( - memoryPerformance.results - ), stageTimes: DataProcessor.processStageTimes(stageTimes.results), - chartColors: DataProcessor.processChartColors( - DataProcessor.processMemoryPerformance(memoryPerformance.results) - ), - error: undefined, - explainAnalyze: DataProcessor.processExplainAnalyze(result.results), + explainAnalyze: result.results + ? DataProcessor.processExplainAnalyze(result.results) + : undefined, + tabID, }; } - async reset() { - await this.disconnect(); - await this.connect(this.connection, this.database); + async reset(tabID: string, serial?: number) { + await this.disconnect(tabID); + await this.connect(tabID, serial, this.connections[tabID].loginDetails); } - async disconnect() { - if ( - !this.runner || - !this.runner.connectionID || - !this.monitor || - !this.monitor.connectionID || - !this.agent - ) { - console.error( - `SqlManager.disconnect: Runner ${Boolean( - this.runner - )}, Monitor ${Boolean(this.monitor)}, Agent ${Boolean(this.agent)}` - ); - return; - } + async disconnect(tabID: string) { + const connection = this.connections[tabID]; + if (!connection) return; + const { agent, monitor, runner, client } = connection; + // Kill monitor manually from agent as client.close won't interfere if monitor has called its procedure - await this.agent?.killConnection(this.monitor.connectionID); - await this.agent?.killConnection(this.runner.connectionID); - await closeConnection(this.client); - SqlManager.log('disconnect(): Disconnected successfully'); + try { + await agent?.killConnection(monitor?.connectionID); + await agent?.killConnection(runner?.connectionID); + await closeConnection(client); + SqlManager.log('disconnect(): Disconnected successfully'); + } catch (error) { + console.error(error); + } } - async cancelQuery() { - this.cancel = true; - await this.disconnect(); + async abortQueries(tabID: string) { + const connection = this.connections[tabID]; + const { agent, monitor, runner } = connection; + await agent?.killQuery(runner?.connectionID); + await agent?.killQuery(monitor?.connectionID); } - async checkThreadStates() { - const runnerState = await this.agent?.checkThreadState( - this.runner?.threadID || '' - ); - const monitorState = await this.agent?.checkThreadState( - this.monitor?.threadID || '' - ); + async cancelRecording(tabID: string) { + this.connections[tabID].cancelled = true; + await this.abortQueries(tabID); + } + + async removeConnection(tabID: string) { + await this.disconnect(tabID); + delete this.connections[tabID]; + console.log(`Connection for tab ${tabID} removed`); + } + + async checkThreadStates(tabID: string) { + const connection = this.connections[tabID]; + const { agent, monitor, runner } = connection; + const runnerState = await agent?.checkThreadState(runner?.threadID || ''); + const monitorState = await agent?.checkThreadState(monitor?.threadID || ''); SqlManager.log( `Runner: ${runnerState?.results?.values}\nMonitor: ${monitorState?.results?.values}` ); } - async executeQuery(query: string) { - if (!this.runner) return []; - const result = await this.runner.executeQuery(query); + async executeQuery(query: string, tabID: string) { + const connection = this.connections[tabID]; + const { runner } = connection; + const result = await runner.executeQuery(query); return result; } diff --git a/backend/recorder/SqlMonitor.ts b/backend/recorder/SqlMonitor.ts index 9065b624516cbca444a5b4be7713ea934287c548..5710a11c604e90e0b01748240386a2c28249ef97 100644 --- a/backend/recorder/SqlMonitor.ts +++ b/backend/recorder/SqlMonitor.ts @@ -11,7 +11,9 @@ export default class SqlMonitor { threadID?: string; - async connect(client?: Client, database?: string) { + serial?: number; + + async connect(client?: Client, database?: string, serial?: number) { SqlMonitor.log('Connecting'); this.session = await createSession(client, database); let resultSet = await this.executeQuery(`SELECT connection_id();`); @@ -20,8 +22,9 @@ export default class SqlMonitor { `SELECT thread_id FROM performance_schema.threads WHERE processlist_id=${this.connectionID}` ); this.threadID = String(resultSet?.results?.values[0]); + this.serial = serial; await this.enableStageLogging(); - await this.createMonitorProcedure(); + await this.createMonitorProcedure(serial); } async enableStageLogging() { @@ -34,11 +37,13 @@ export default class SqlMonitor { ); } - async createMonitorProcedure() { - await this.executeQuery('DROP PROCEDURE IF EXISTS monitor_connection;'); + async createMonitorProcedure(serial?: number) { + await this.executeQuery( + `DROP PROCEDURE IF EXISTS monitor_connection${serial};` + ); // Create monitoring procedure await this.executeQuery( - `CREATE PROCEDURE monitor_connection( + `CREATE PROCEDURE monitor_connection${serial}( IN conn_id BIGINT UNSIGNED, IN stage_ignore_before BIGINT UNSIGNED, IN timestep_size FLOAT @@ -50,8 +55,8 @@ export default class SqlMonitor { SET thd_id = (SELECT thread_id FROM performance_schema.threads WHERE processlist_id=conn_id); - DROP TABLE IF EXISTS monitoring_data; - CREATE TABLE monitoring_data ( + DROP TABLE IF EXISTS monitoring_data${serial}; + CREATE TABLE monitoring_data${serial} ( TS DATETIME(6), THREAD_ID BIGINT UNSIGNED, EVENT_NAME VARCHAR(128), @@ -73,7 +78,7 @@ export default class SqlMonitor { REPEAT SET state = (SELECT PROCESSLIST_COMMAND FROM performance_schema.threads WHERE THREAD_ID=thd_id); - INSERT INTO monitoring_data + INSERT INTO monitoring_data${serial} SELECT NOW(6) AS 'TS', THREAD_ID, @@ -95,8 +100,8 @@ export default class SqlMonitor { SET stage_min_ts = (SELECT MIN(timer_start) FROM performance_schema.events_stages_history_long WHERE THREAD_ID=thd_id AND timer_start > stage_ignore_before); - DROP TABLE IF EXISTS monitoring_stages; - CREATE TABLE monitoring_stages AS + DROP TABLE IF EXISTS monitoring_stages${serial}; + CREATE TABLE monitoring_stages${serial} AS SELECT EVENT_NAME, SOURCE, @@ -126,27 +131,31 @@ export default class SqlMonitor { `Monitoring with timestep of ${timestep} seconds, connectionID ${connectionID}` ); await this.executeQuery( - `call monitor_connection(${connectionID}, ${stageIgnoreBeforeResult}, ${timestep});` + `call monitor_connection${this.serial}(${connectionID}, ${stageIgnoreBeforeResult}, ${timestep});` ); } async dumpData() { await this.executeQuery( - 'SET @min_ts = (SELECT UNIX_TIMESTAMP(MIN(TS)) FROM monitoring_data);' + `SET @min_ts = (SELECT UNIX_TIMESTAMP(MIN(TS)) FROM monitoring_data${this.serial});` ); const data = await this.executeQuery( - `SELECT UNIX_TIMESTAMP(TS) - @min_ts, EVENT_NAME, CURRENT_NUMBER_OF_BYTES_USED FROM monitoring_data ORDER BY 1;` + `SELECT UNIX_TIMESTAMP(TS) - @min_ts, EVENT_NAME, CURRENT_NUMBER_OF_BYTES_USED FROM monitoring_data${this.serial} ORDER BY 1;` + ); + await this.executeQuery( + `DROP TABLE IF EXISTS monitoring_data${this.serial};` ); - await this.executeQuery(`DROP TABLE IF EXISTS monitoring_data;`); return data; } async getStageTimes() { const stageTimesRaw = await this.executeQuery( - `select event_name, source, start, end from monitoring_stages;` + `select event_name, source, start, end from monitoring_stages${this.serial};` + ); + await this.executeQuery( + `DROP TABLE IF EXISTS monitoring_stages${this.serial};` ); - await this.executeQuery(`DROP TABLE IF EXISTS monitoring_stages;`); return stageTimesRaw; } diff --git a/backend/recorder/SqlRunner.ts b/backend/recorder/SqlRunner.ts index ccea83e7616c2d46115bc9fb35edc859f6c179ca..60055525596c540c667e5be032af91d361e96bf5 100644 --- a/backend/recorder/SqlRunner.ts +++ b/backend/recorder/SqlRunner.ts @@ -1,4 +1,5 @@ -import { URI, Session } from '@mysql/xdevapi'; +import { Session } from '@mysql/xdevapi'; +import { LoginDetails } from '../types/LoginDetails'; import { createConnection, createSession, runQuery } from '../utils/mysql'; /** @@ -11,11 +12,11 @@ export default class SqlRunner { threadID?: string; - async connect(connection?: URI, database?: string) { - const client = await createConnection(connection, { + async connect(loginDetails?: LoginDetails) { + const client = await createConnection(loginDetails, { pooling: { enabled: true }, }); - this.session = await createSession(client, database); + this.session = await createSession(client, loginDetails?.database); let resultSet = await runQuery(`SELECT connection_id();`, this.session); this.connectionID = String(resultSet?.results?.values[0]); resultSet = await runQuery( diff --git a/backend/utils/LoginDetails.ts b/backend/types/LoginDetails.ts similarity index 100% rename from backend/utils/LoginDetails.ts rename to backend/types/LoginDetails.ts diff --git a/backend/utils/FileIO.ts b/backend/utils/FileIO.ts index 8cc94ed2d50f7f910f384867133f130e2fff20cd..775733be8c95689c81be7ddf1eea33deaa2e5a8c 100644 --- a/backend/utils/FileIO.ts +++ b/backend/utils/FileIO.ts @@ -1,23 +1,37 @@ import * as fs from 'fs'; -import { LoginDetails } from './LoginDetails'; +import path from 'path'; +import process from 'process'; +import { Recording } from '../../app/types/Recording'; +import { LoginDetails } from '../types/LoginDetails'; // fs docs: https://nodejs.org/api/fs.html#fs_file_paths export default class FileIO { static async saveLoginDetails(loginDetails: LoginDetails) { - const loginDetailsLocation = 'loginDetails.json'; + const loginDetailsLocation = path.join( + this.getAppDataLocation(), + 'loginDetails.json' + ); const fixedLoginDetails = loginDetails; if (!loginDetails.savePassword) { fixedLoginDetails.password = ''; } + if (!fs.existsSync(this.getAppDataLocation())) { + console.log('Folder did not exist, creating'); + await fs.promises.mkdir(this.getAppDataLocation()); + } await fs.promises.writeFile( loginDetailsLocation, JSON.stringify(fixedLoginDetails, undefined, 2) ); + return true; } static async loadLoginDetails(): Promise<LoginDetails | undefined> { - const loginDetailsLocation = 'loginDetails.json'; + const loginDetailsLocation = path.join( + this.getAppDataLocation(), + 'loginDetails.json' + ); if (fs.existsSync(loginDetailsLocation)) { const data = await fs.promises.readFile(loginDetailsLocation); return JSON.parse(String(data)); @@ -25,4 +39,64 @@ export default class FileIO { console.log(`${loginDetailsLocation} does not exist!`); return undefined; } + + static async saveRecordingDetails(recDetails: Recording, filename: string) { + const dest = path.join(this.getAppDataLocation(), 'Recordings/'); + const savePath = `${dest + filename}.json`; + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest); + } + await fs.promises.writeFile( + savePath, + JSON.stringify(recDetails, undefined, 2) + ); + return savePath; + } + + static async loadRecording(filepath: string): Promise<Recording> { + try { + if (fs.existsSync(filepath)) { + const data = await fs.promises.readFile(filepath); + const object = JSON.parse(String(data)); + const castObject = object as Recording; + if ( + !( + castObject.uuid && + castObject.explainAnalyze !== undefined && + castObject.label !== undefined && + castObject.query !== undefined + ) + ) { + throw new Error('File was not a recording'); + } + return castObject; + } + throw new Error(`${filepath} does not exist! `); + } catch (error) { + throw new Error('Filetype not supported'); + } + } + + static getAppDataLocation() { + const { platform } = process; + if (platform === 'darwin' && process.env.HOME) { + return path.join( + process.env.HOME, + 'Library', + 'Application Support', + 'MySQL Query Profiler' + ); + } + if (platform === 'win32' && process.env.APPDATA) { + return path.join(process.env.APPDATA, 'MySQL Query Profiler'); + } + if (platform === 'linux' && process.env.HOME) { + return path.join(process.env.HOME, '.MySQL Query Profiler'); + } + + console.log( + 'Unrecognized platform: App data will be written to current location if possible' + ); + return ''; + } } diff --git a/package.json b/package.json index 48174a9ebbb95491a876af70944f3d963c9d6107..bc5253ea899b1975d2e4d0e240b5f81f56c88f0d 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "@types/history": "^4.7.6", "@types/jest": "^26.0.10", "@types/mysql": "^2.15.15", - "@types/node": "12", + "@types/node": "14.11.8", "@types/react": "^16.9.49", "@types/react-color": "^3.0.4", "@types/react-dom": "^16.9.8", @@ -175,23 +175,23 @@ "chalk": "^4.1.0", "core-js": "^3.6.5", "cross-env": "^7.0.2", - "css-loader": "^3.6.0", + "css-loader": "^4.3.0", "detect-port": "^1.3.0", "electron": "^10", "electron-builder": "^22.3.6", "electron-devtools-installer": "^3.1.1", - "electron-rebuild": "^1.10.0", + "electron-rebuild": "^2.2.0", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", "enzyme-to-json": "^3.5.0", "eslint": "^7.9.0", "eslint-config-airbnb": "^18.2.0", - "eslint-config-airbnb-typescript": "^9.0.0", + "eslint-config-airbnb-typescript": "^11.0.0", "eslint-config-prettier": "^6.11.0", - "eslint-import-resolver-webpack": "^0.12.2", + "eslint-import-resolver-webpack": "^0.13.0", "eslint-plugin-compat": "^3.8.0", "eslint-plugin-import": "^2.22.0", - "eslint-plugin-jest": "^23.18.0", + "eslint-plugin-jest": "^24.1.0", "eslint-plugin-jsx-a11y": "6.3.1", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-promise": "^4.2.1", @@ -203,25 +203,25 @@ "identity-obj-proxy": "^3.0.0", "jest": "^26.1.0", "lint-staged": "^10.2.11", - "mini-css-extract-plugin": "^0.9.0", + "mini-css-extract-plugin": "^1.0.0", "opencollective-postinstall": "^2.0.3", "optimize-css-assets-webpack-plugin": "^5.0.3", "prettier": "^2.1.1", "react-refresh": "^0.8.3", "react-test-renderer": "^16.12.0", "rimraf": "^3.0.0", - "sass-loader": "^9.0.2", - "style-loader": "^1.2.1", + "sass-loader": "^10.0.3", + "style-loader": "^2.0.0", "stylelint": "^13.6.1", "stylelint-config-prettier": "^8.0.2", "stylelint-config-standard": "^20.0.0", - "terser-webpack-plugin": "^3.0.7", + "terser-webpack-plugin": "^4.2.3", "testcafe": "^1.8.8", "testcafe-browser-provider-electron": "^0.0.15", "testcafe-react-selectors": "^4.0.0", "ts-node": "^9.0.0", "type-fest": "^0.17.0", - "typescript": "^3.9.7", + "typescript": "^4.0.3", "typings-for-css-modules-loader": "^1.7.0", "url-loader": "^4.1.0", "webpack": "^4.44.2", @@ -241,13 +241,13 @@ "electron-log": "^4.2.4", "electron-react-devtools": "^0.5.3", "electron-updater": "^4.3.5", - "history": "^4.7.2", + "history": "^5.0.0", "lodash": "^4.17.20", "react": "^16.13.1", + "react-color": "^2.18.1", "react-dom": "^16.12.0", "react-flame-graph": "^1.4.0", "react-hot-loader": "^4.12.21", - "react-color": "^2.18.1", "react-json-view": "^1.19.1", "react-panelgroup": "^1.0.12", "react-router-dom": "^5.2.0", @@ -257,7 +257,8 @@ "regenerator-runtime": "^0.13.7", "source-map-support": "^0.5.19", "use-deep-compare-effect": "^1.4.0", - "uuid": "^8.3.0" + "uuid": "^8.3.0", + "victory": "^35.3.0" }, "devEngines": { "node": ">=7.x", diff --git a/resources/Icon.icns b/resources/Icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..86f02e6756137dbb1750d6a583c45dc9215bd397 Binary files /dev/null and b/resources/Icon.icns differ diff --git a/resources/icon.ico b/resources/icon.ico index 15e5d78bb470d7236df9db38259c730560d1141d..48c7421ab68bf5359f6ab071a3715081eee2d6e2 100644 Binary files a/resources/icon.ico and b/resources/icon.ico differ diff --git a/resources/icon.png b/resources/icon.png index 2c0f60050d903da7e723646396a896be9b90a9a8..64240740d32f4e15777fdc9c207a68504bf32237 100644 Binary files a/resources/icon.png and b/resources/icon.png differ diff --git a/resources/icons/1024x1024.png b/resources/icons/1024x1024.png new file mode 100644 index 0000000000000000000000000000000000000000..64240740d32f4e15777fdc9c207a68504bf32237 Binary files /dev/null and b/resources/icons/1024x1024.png differ diff --git a/resources/icons/128x128.png b/resources/icons/128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..5d7137fa50f0b3917d14be6f4ddd77ac67bccda8 Binary files /dev/null and b/resources/icons/128x128.png differ diff --git a/resources/icons/16x16.png b/resources/icons/16x16.png index b2a9c915f2c8059368c8e6a4c14168756e886745..15dad1020732bde31b69dc2c7922b2203b73d104 100644 Binary files a/resources/icons/16x16.png and b/resources/icons/16x16.png differ diff --git a/resources/icons/24x24.png b/resources/icons/24x24.png new file mode 100644 index 0000000000000000000000000000000000000000..f31ebcdbde7d58f5057133886a433c1ce41b10c2 Binary files /dev/null and b/resources/icons/24x24.png differ diff --git a/resources/icons/256x256.png b/resources/icons/256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..5a633f2461d3b1328c3589c5580b311f64d88e0d Binary files /dev/null and b/resources/icons/256x256.png differ diff --git a/resources/icons/32x32.png b/resources/icons/32x32.png index 2fca10cdbc0186242fbec8f6edbbdad44bc64f0f..bf32050f3149033e264030678da808f2d8ad3742 100644 Binary files a/resources/icons/32x32.png and b/resources/icons/32x32.png differ diff --git a/resources/icons/48x48.png b/resources/icons/48x48.png index 581a1367d311e6b6cdffe536b6f3936c1f956230..b60e058601303accab200d1b177695d4ac5fde0e 100644 Binary files a/resources/icons/48x48.png and b/resources/icons/48x48.png differ diff --git a/resources/icons/512x512.png b/resources/icons/512x512.png index 6ff3ba1ecab12c8d4dbfb34dc88b3c6a7350ca27..60d4fba78f5f8696c3982fa6a88431c225f33a00 100644 Binary files a/resources/icons/512x512.png and b/resources/icons/512x512.png differ diff --git a/resources/icons/64x64.png b/resources/icons/64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..cb71ab748a6971a625e3a72313ce391fb1e3e4e8 Binary files /dev/null and b/resources/icons/64x64.png differ diff --git a/resources/icons/96x96.png b/resources/icons/96x96.png index ecc2b8a558bf6dd2089bf90cc5c92ece116cd4ed..cd7ad02fe05128cb76395428cd2c968f635f6ddb 100644 Binary files a/resources/icons/96x96.png and b/resources/icons/96x96.png differ diff --git a/yarn.lock b/yarn.lock index 8d6bf039776667ca71bdb72e180a7a7e851633a8..4dc5431d2b0569ea63800471fe07c0e8b6662a46 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,7 +42,7 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/core@>=7.9.0", "@babel/core@^7.11.1": +"@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.7.5": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== @@ -64,29 +64,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.11.4" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.4.tgz#4301dfdfafa01eeb97f1896c5501a3f0655d4229" - integrity sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.4" - "@babel/helper-module-transforms" "^7.11.0" - "@babel/helpers" "^7.10.4" - "@babel/parser" "^7.11.4" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.11.0" - "@babel/types" "^7.11.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@^7.11.0", "@babel/generator@^7.11.4", "@babel/generator@^7.11.5", "@babel/generator@^7.11.6": +"@babel/generator@^7.11.0", "@babel/generator@^7.11.5", "@babel/generator@^7.11.6": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== @@ -319,16 +297,16 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.7.0": - version "7.11.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.4.tgz#6fa1a118b8b0d80d0267b719213dc947e88cc0ca" - integrity sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA== - -"@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.4", "@babel/parser@^7.11.5": +"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.5": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== +"@babel/parser@^7.7.0": + version "7.11.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.4.tgz#6fa1a118b8b0d80d0267b719213dc947e88cc0ca" + integrity sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA== + "@babel/plugin-proposal-async-generator-functions@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" @@ -1105,7 +1083,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.11.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736" integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw== @@ -1121,22 +1099,7 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.0.tgz#9b996ce1b98f53f7c3e4175115605d56ed07dd24" - integrity sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.0" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/parser" "^7.11.0" - "@babel/types" "^7.11.0" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - -"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.0", "@babel/traverse@^7.11.5": +"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== @@ -1151,16 +1114,22 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.7.0": +"@babel/traverse@^7.7.0": version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" - integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA== + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.0.tgz#9b996ce1b98f53f7c3e4175115605d56ed07dd24" + integrity sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.0" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.0" + "@babel/types" "^7.11.0" + debug "^4.1.0" + globals "^11.1.0" lodash "^4.17.19" - to-fast-properties "^2.0.0" -"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== @@ -1169,6 +1138,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.7.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" + integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1258,93 +1236,93 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.3.0.tgz#ed04063efb280c88ba87388b6f16427c0a85c856" - integrity sha512-/5Pn6sJev0nPUcAdpJHMVIsA8sKizL2ZkcKPE5+dJrCccks7tcM7c9wbgHudBJbxXLoTbqsHkG1Dofoem4F09w== +"@jest/console@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.5.2.tgz#94fc4865b1abed7c352b5e21e6c57be4b95604a6" + integrity sha512-lJELzKINpF1v74DXHbCRIkQ/+nUV1M+ntj+X1J8LxCgpmJZjfLmhFejiMSbjjD66fayxl5Z06tbs3HMyuik6rw== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^26.3.0" - jest-util "^26.3.0" + jest-message-util "^26.5.2" + jest-util "^26.5.2" slash "^3.0.0" -"@jest/core@^26.4.2": - version "26.4.2" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.4.2.tgz#85d0894f31ac29b5bab07aa86806d03dd3d33edc" - integrity sha512-sDva7YkeNprxJfepOctzS8cAk9TOekldh+5FhVuXS40+94SHbiicRO1VV2tSoRtgIo+POs/Cdyf8p76vPTd6dg== +"@jest/core@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.5.2.tgz#e39f14676f4ba4632ecabfdc374071ab22131f22" + integrity sha512-LLTo1LQMg7eJjG/+P1NYqFof2B25EV1EqzD5FonklihG4UJKiK2JBIvWonunws6W7e+DhNLoFD+g05tCY03eyA== dependencies: - "@jest/console" "^26.3.0" - "@jest/reporters" "^26.4.1" - "@jest/test-result" "^26.3.0" - "@jest/transform" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/console" "^26.5.2" + "@jest/reporters" "^26.5.2" + "@jest/test-result" "^26.5.2" + "@jest/transform" "^26.5.2" + "@jest/types" "^26.5.2" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" - jest-changed-files "^26.3.0" - jest-config "^26.4.2" - jest-haste-map "^26.3.0" - jest-message-util "^26.3.0" + jest-changed-files "^26.5.2" + jest-config "^26.5.2" + jest-haste-map "^26.5.2" + jest-message-util "^26.5.2" jest-regex-util "^26.0.0" - jest-resolve "^26.4.0" - jest-resolve-dependencies "^26.4.2" - jest-runner "^26.4.2" - jest-runtime "^26.4.2" - jest-snapshot "^26.4.2" - jest-util "^26.3.0" - jest-validate "^26.4.2" - jest-watcher "^26.3.0" + jest-resolve "^26.5.2" + jest-resolve-dependencies "^26.5.2" + jest-runner "^26.5.2" + jest-runtime "^26.5.2" + jest-snapshot "^26.5.2" + jest-util "^26.5.2" + jest-validate "^26.5.2" + jest-watcher "^26.5.2" micromatch "^4.0.2" p-each-series "^2.1.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.3.0.tgz#e6953ab711ae3e44754a025f838bde1a7fd236a0" - integrity sha512-EW+MFEo0DGHahf83RAaiqQx688qpXgl99wdb8Fy67ybyzHwR1a58LHcO376xQJHfmoXTu89M09dH3J509cx2AA== +"@jest/environment@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.5.2.tgz#eba3cfc698f6e03739628f699c28e8a07f5e65fe" + integrity sha512-YjhCD/Zhkz0/1vdlS/QN6QmuUdDkpgBdK4SdiVg4Y19e29g4VQYN5Xg8+YuHjdoWGY7wJHMxc79uDTeTOy9Ngw== dependencies: - "@jest/fake-timers" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/fake-timers" "^26.5.2" + "@jest/types" "^26.5.2" "@types/node" "*" - jest-mock "^26.3.0" + jest-mock "^26.5.2" -"@jest/fake-timers@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.3.0.tgz#f515d4667a6770f60ae06ae050f4e001126c666a" - integrity sha512-ZL9ytUiRwVP8ujfRepffokBvD2KbxbqMhrXSBhSdAhISCw3gOkuntisiSFv+A6HN0n0fF4cxzICEKZENLmW+1A== +"@jest/fake-timers@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.5.2.tgz#1291ac81680ceb0dc7daa1f92c059307eea6400a" + integrity sha512-09Hn5Oraqt36V1akxQeWMVL0fR9c6PnEhpgLaYvREXZJAh2H2Y+QLCsl0g7uMoJeoWJAuz4tozk1prbR1Fc1sw== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" "@sinonjs/fake-timers" "^6.0.1" "@types/node" "*" - jest-message-util "^26.3.0" - jest-mock "^26.3.0" - jest-util "^26.3.0" + jest-message-util "^26.5.2" + jest-mock "^26.5.2" + jest-util "^26.5.2" -"@jest/globals@^26.4.2": - version "26.4.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.4.2.tgz#73c2a862ac691d998889a241beb3dc9cada40d4a" - integrity sha512-Ot5ouAlehhHLRhc+sDz2/9bmNv9p5ZWZ9LE1pXGGTCXBasmi5jnYjlgYcYt03FBwLmZXCZ7GrL29c33/XRQiow== +"@jest/globals@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.5.2.tgz#c333f82c29e19ecb609a75d1a532915a5c956c59" + integrity sha512-9PmnFsAUJxpPt1s/stq02acS1YHliVBDNfAWMe1bwdRr1iTCfhbNt3ERQXrO/ZfZSweftoA26Q/2yhSVSWQ3sw== dependencies: - "@jest/environment" "^26.3.0" - "@jest/types" "^26.3.0" - expect "^26.4.2" + "@jest/environment" "^26.5.2" + "@jest/types" "^26.5.2" + expect "^26.5.2" -"@jest/reporters@^26.4.1": - version "26.4.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.4.1.tgz#3b4d6faf28650f3965f8b97bc3d114077fb71795" - integrity sha512-aROTkCLU8++yiRGVxLsuDmZsQEKO6LprlrxtAuzvtpbIFl3eIjgIf3EUxDKgomkS25R9ZzwGEdB5weCcBZlrpQ== +"@jest/reporters@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.5.2.tgz#0f1c900c6af712b46853d9d486c9c0382e4050f6" + integrity sha512-zvq6Wvy6MmJq/0QY0YfOPb49CXKSf42wkJbrBPkeypVa8I+XDxijvFuywo6TJBX/ILPrdrlE/FW9vJZh6Rf9vA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.3.0" - "@jest/test-result" "^26.3.0" - "@jest/transform" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/console" "^26.5.2" + "@jest/test-result" "^26.5.2" + "@jest/transform" "^26.5.2" + "@jest/types" "^26.5.2" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" @@ -1355,10 +1333,10 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^26.3.0" - jest-resolve "^26.4.0" - jest-util "^26.3.0" - jest-worker "^26.3.0" + jest-haste-map "^26.5.2" + jest-resolve "^26.5.2" + jest-util "^26.5.2" + jest-worker "^26.5.0" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" @@ -1367,51 +1345,51 @@ optionalDependencies: node-notifier "^8.0.0" -"@jest/source-map@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.3.0.tgz#0e646e519883c14c551f7b5ae4ff5f1bfe4fc3d9" - integrity sha512-hWX5IHmMDWe1kyrKl7IhFwqOuAreIwHhbe44+XH2ZRHjrKIh0LO5eLQ/vxHFeAfRwJapmxuqlGAEYLadDq6ZGQ== +"@jest/source-map@^26.5.0": + version "26.5.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.5.0.tgz#98792457c85bdd902365cd2847b58fff05d96367" + integrity sha512-jWAw9ZwYHJMe9eZq/WrsHlwF8E3hM9gynlcDpOyCb9bR8wEd9ZNBZCi7/jZyzHxC7t3thZ10gO2IDhu0bPKS5g== dependencies: callsites "^3.0.0" graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.3.0.tgz#46cde01fa10c0aaeb7431bf71e4a20d885bc7fdb" - integrity sha512-a8rbLqzW/q7HWheFVMtghXV79Xk+GWwOK1FrtimpI5n1la2SY0qHri3/b0/1F0Ve0/yJmV8pEhxDfVwiUBGtgg== +"@jest/test-result@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.5.2.tgz#cc1a44cfd4db2ecee3fb0bc4e9fe087aa54b5230" + integrity sha512-E/Zp6LURJEGSCWpoMGmCFuuEI1OWuI3hmZwmULV0GsgJBh7u0rwqioxhRU95euUuviqBDN8ruX/vP/4bwYolXw== dependencies: - "@jest/console" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/console" "^26.5.2" + "@jest/types" "^26.5.2" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^26.4.2": - version "26.4.2" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.4.2.tgz#58a3760a61eec758a2ce6080201424580d97cbba" - integrity sha512-83DRD8N3M0tOhz9h0bn6Kl6dSp+US6DazuVF8J9m21WAp5x7CqSMaNycMP0aemC/SH/pDQQddbsfHRTBXVUgog== +"@jest/test-sequencer@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.5.2.tgz#c4559c7e134b27b020317303ee5399bf62917a4b" + integrity sha512-XmGEh7hh07H2B8mHLFCIgr7gA5Y6Hw1ZATIsbz2fOhpnQ5AnQtZk0gmP0Q5/+mVB2xygO64tVFQxOajzoptkNA== dependencies: - "@jest/test-result" "^26.3.0" + "@jest/test-result" "^26.5.2" graceful-fs "^4.2.4" - jest-haste-map "^26.3.0" - jest-runner "^26.4.2" - jest-runtime "^26.4.2" + jest-haste-map "^26.5.2" + jest-runner "^26.5.2" + jest-runtime "^26.5.2" -"@jest/transform@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.3.0.tgz#c393e0e01459da8a8bfc6d2a7c2ece1a13e8ba55" - integrity sha512-Isj6NB68QorGoFWvcOjlUhpkT56PqNIsXKR7XfvoDlCANn/IANlh8DrKAA2l2JKC3yWSMH5wS0GwuQM20w3b2A== +"@jest/transform@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.5.2.tgz#6a0033a1d24316a1c75184d010d864f2c681bef5" + integrity sha512-AUNjvexh+APhhmS8S+KboPz+D3pCxPvEAGduffaAJYxIFxGi/ytZQkrqcKDUU0ERBAo5R7087fyOYr2oms1seg== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" babel-plugin-istanbul "^6.0.0" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.4" - jest-haste-map "^26.3.0" + jest-haste-map "^26.5.2" jest-regex-util "^26.0.0" - jest-util "^26.3.0" + jest-util "^26.5.2" micromatch "^4.0.2" pirates "^4.0.1" slash "^3.0.0" @@ -1428,10 +1406,10 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" -"@jest/types@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71" - integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ== +"@jest/types@^26.5.2": + version "26.5.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.5.2.tgz#44c24f30c8ee6c7f492ead9ec3f3c62a5289756d" + integrity sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" @@ -1439,6 +1417,13 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@malept/cross-spawn-promise@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.0.tgz#258fde4098f5004a56db67c35f33033af64810f6" + integrity sha512-GeIK5rfU1Yd7BZJQPTGZMMmcZy5nhRToPXZcjaDwQDRSewdhp648GT2E4dh+L7+Io7AOW6WQ+GR44QSzja4qxg== + dependencies: + cross-spawn "^7.0.1" + "@material-ui/core@^4.11.0": version "4.11.0" resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.11.0.tgz#b69b26e4553c9e53f2bfaf1053e216a0af9be15a" @@ -1587,6 +1572,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sindresorhus/is@^3.1.1": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-3.1.2.tgz#548650de521b344e3781fbdb0ece4aa6f729afb8" + integrity sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ== + "@sinonjs/commons@^1.7.0": version "1.8.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" @@ -1623,15 +1613,22 @@ dependencies: defer-to-connect "^1.0.1" +"@szmarczak/http-timer@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== + dependencies: + defer-to-connect "^2.0.0" + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": - version "7.1.9" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" - integrity sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw== + version "7.1.10" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40" + integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -1640,50 +1637,55 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.1" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.1.tgz#4901767b397e8711aeb99df8d396d7ba7b7f0e04" - integrity sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew== + version "7.6.2" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" + integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" - integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== + version "7.0.3" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.3.tgz#b8aaeba0a45caca7b56a5de9459872dde3727214" + integrity sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.0.13" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.13.tgz#1874914be974a492e1b4cb00585cabb274e8ba18" - integrity sha512-i+zS7t6/s9cdQvbqKDARrcbrPvtJGlbYsMkazo03nTAK3RX9FNrLllXys22uiTGJapPOTZTQ35nHh4ISph4SLQ== +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03" + integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A== dependencies: "@babel/types" "^7.3.0" -"@types/cheerio@*": - version "0.22.21" - resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3" - integrity sha512-aGI3DfswwqgKPiEOTaiHV2ZPC9KEhprpgEbJnv0fZl3SGX0cGgEva1126dGrMC6AJM6v/aihlUgJn9M5DbDZ/Q== +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" "@types/node" "*" + "@types/responselike" "*" -"@types/color-name@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" - integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/cheerio@*", "@types/cheerio@^0.22.22": + version "0.22.22" + resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.22.tgz#ae71cf4ca59b8bbaf34c99af7a5d6c8894988f5f" + integrity sha512-05DYX4zU96IBfZFY+t3Mh88nlwSMtmmzSYaQkKN48T495VV1dkHSah6qYyDTN5ngaS0i0VonH37m+RuzSM0YiA== + dependencies: + "@types/node" "*" -"@types/d3-path@*": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" - integrity sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA== +"@types/d3-path@^1": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c" + integrity sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ== -"@types/d3-shape@*": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.2.tgz#a41d9d6b10d02e221696b240caf0b5d0f5a588ec" - integrity sha512-LtD8EaNYCaBRzHzaAiIPrfcL3DdIysc81dkGlQvv7WQP3+YXV7b0JJTtR1U3bzeRieS603KF4wUo+ZkJVenh8w== +"@types/d3-shape@^1": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.4.tgz#5a6d8c3026ba8e8a1a985bda8da40acfc9b7b079" + integrity sha512-fxmOjs+UqNQGpztD5BOo+KriE0jLFrBP4Ct++0QExv/xfDOT1cpcMxgsZ+5qPmnR0t+GjbwAe1Um1PHpv3G4oA== dependencies: - "@types/d3-path" "*" + "@types/d3-path" "^1" "@types/debug@^4.1.5": version "4.1.5" @@ -1711,9 +1713,9 @@ "@types/react" "*" "@types/enzyme@^3.10.5": - version "3.10.6" - resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.6.tgz#a34a6b09ff732be29c194dc8d786555f4717fd85" - integrity sha512-Jxyn2U+UfhmrE7EaeZYfV1sPA5OEDuZmg9Lxj8TxOnfCn71flf0Cn1136LWdCVYj7yapLFmUWqKCcgplWNZCJg== + version "3.10.7" + resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.7.tgz#ebdf3b972d293095e09af479e36c772025285e3a" + integrity sha512-J+0wduPGAkzOvW7sr6hshGv1gBI3WXLRTczkRKzVPxLP3xAkYxZmvvagSBPw8Z452fZ8TGUxCmAXcb44yLQksw== dependencies: "@types/cheerio" "*" "@types/react" "*" @@ -1723,11 +1725,6 @@ resolved "https://registry.yarnpkg.com/@types/error-stack-parser/-/error-stack-parser-1.3.18.tgz#e01c9f8c85ca83b610320c62258b0c9026ade0f7" integrity sha1-4ByfjIXKg7YQMgxiJYsMkCat4Pc= -"@types/eslint-visitor-keys@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" - integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== - "@types/estree@^0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -1755,16 +1752,16 @@ dependencies: "@types/node" "*" -"@types/history@*": - version "4.7.7" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.7.tgz#613957d900fab9ff84c8dfb24fa3eef0c2a40896" - integrity sha512-2xtoL22/3Mv6a70i4+4RB7VgbDDORoWwjcqeNysojZA0R7NK17RbY5Gof/2QiFfJgX+KkWghbwJ+d/2SB8Ndzg== - -"@types/history@^4.7.6": +"@types/history@*", "@types/history@^4.7.6": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" @@ -1800,7 +1797,7 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5": +"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": version "7.0.6" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== @@ -1810,6 +1807,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/keyv@*": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + "@types/lodash@^4.14.72": version "4.14.161" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.161.tgz#a21ca0777dabc6e4f44f3d07f37b765f54188b18" @@ -1832,25 +1836,20 @@ dependencies: "@types/node" "*" -"@types/node@*": - version "14.11.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.1.tgz#56af902ad157e763f9ba63d671c39cda3193c835" - integrity sha512-oTQgnd0hblfLsJ6BvJzzSL+Inogp3lq9fGgqRkMB/ziKMgEUaFl801OncOzUmalfzt14N0oPHMK47ipl+wbTIw== - -"@types/node@12": - version "12.12.62" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.62.tgz#733923d73669188d35950253dd18a21570085d2b" - integrity sha512-qAfo81CsD7yQIM9mVyh6B/U47li5g7cfpVQEDMfQeF8pSZVwzbhwU3crc0qG4DmpsebpJPR49AKOExQyJ05Cpg== +"@types/node@*", "@types/node@14.11.8": + version "14.11.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.8.tgz#fe2012f2355e4ce08bca44aeb3abbb21cf88d33f" + integrity sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw== "@types/node@^10.12.19": - version "10.17.35" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.35.tgz#58058f29b870e6ae57b20e4f6e928f02b7129f56" - integrity sha512-gXx7jAWpMddu0f7a+L+txMplp3FnHl53OhQIF9puXKq3hDGY/GjH+MF04oWnV/adPSCrbtHumDCFwzq2VhltWA== + version "10.17.39" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.39.tgz#ce1122758d0608de8303667cebf171f44192629b" + integrity sha512-dJLCxrpQmgyxYGcl0Ae9MTsQgI22qHHcGFj/8VKu7McJA5zQpnuGjoksnxbo1JxSjW/Nahnl13W8MYZf01CZHA== "@types/node@^12.0.12": - version "12.12.54" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.54.tgz#a4b58d8df3a4677b6c08bfbc94b7ad7a7a5f82d1" - integrity sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w== + version "12.12.67" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.67.tgz#4f86badb292e822e3b13730a1f9713ed2377f789" + integrity sha512-R48tgL2izApf+9rYNH+3RBMbRpPeW3N8f0I9HMhggeq4UXwBDqumJ14SDs4ctTMhG11pIOduZ4z3QWGOiMc9Vg== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1863,9 +1862,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.2.tgz#5bb52ee68d0f8efa9cc0099920e56be6cc4e37f3" - integrity sha512-IkVfat549ggtkZUthUzEX49562eGikhSYeVGX97SkMFn+sTZrgRewXjQ4tPKFPCykZHkX1Zfd9OoELGqKU2jJA== + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.1.tgz#be148756d5480a84cde100324c03a86ae5739fb5" + integrity sha512-2zs+O+UkDsJ1Vcp667pd3f8xearMdopz/z54i99wtRDI5KLmngk7vlrYZD0ZjKHaROR03EznlBbVY9PfAEyJIQ== "@types/prop-types@*": version "15.7.3" @@ -1900,9 +1899,9 @@ "@types/react" "*" "@types/react-router-dom@^5.1.5": - version "5.1.5" - resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090" - integrity sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw== + version "5.1.6" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.6.tgz#07b14e7ab1893a837c8565634960dc398564b1fb" + integrity sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g== dependencies: "@types/history" "*" "@types/react" "*" @@ -1946,9 +1945,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^16.9.49": - version "16.9.49" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.49.tgz#09db021cf8089aba0cdb12a49f8021a69cce4872" - integrity sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g== + version "16.9.51" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.51.tgz#f8aa51ffa9996f1387f63686696d9b59713d2b60" + integrity sha512-lQa12IyO+DMlnSZ3+AGHRUiUcpK47aakMMoBG8f7HGxJT8Yfe+WE128HIXaHOHVPReAW0oDS3KAI0JI2DDe1PQ== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -1961,11 +1960,11 @@ "@types/react" "*" "@types/recharts@^1.8.15": - version "1.8.15" - resolved "https://registry.yarnpkg.com/@types/recharts/-/recharts-1.8.15.tgz#02bc06085c9a31a58c00194d15377b45cf506bbf" - integrity sha512-ApCfDT/R8RCbZTcl0iqLiKsxVdzE3GzoawTgJUHuQOz6ZXhsPVfp7CNSKI2s3zFwrRRsgmpv2AEcbcZceNHg4w== + version "1.8.16" + resolved "https://registry.yarnpkg.com/@types/recharts/-/recharts-1.8.16.tgz#3ac3f5513ed61152910f2e828157b21a2761df22" + integrity sha512-xBXjOsSJJVP2xGtq/kML4rHGyKxA4x8ZqIU7iwbmWrperpSXzoQ1PWjqf4cBdmcLAWaidDHPJxUVHkZr3R/PEA== dependencies: - "@types/d3-shape" "*" + "@types/d3-shape" "^1" "@types/react" "*" "@types/regenerator-runtime@^0.13.0": @@ -1973,6 +1972,13 @@ resolved "https://registry.yarnpkg.com/@types/regenerator-runtime/-/regenerator-runtime-0.13.0.tgz#8b81e6047da0fbb71927c23d749e2a9edf9aad9b" integrity sha512-U5osy6qZU3lOPGQRHGDIAt/hC/jAE7zP4Aq258UMXnF8/htmZ+72ALrSsmWTtqML7lCztKkKaPz6Pb5XHJF0Vw== +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/semver@^7.3.1": version "7.3.4" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.4.tgz#43d7168fec6fa0988bb1a513a697b29296721afb" @@ -1983,10 +1989,10 @@ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== -"@types/stack-utils@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" - integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/stack-utils@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" + integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== "@types/tapable@*": version "1.0.6" @@ -2048,7 +2054,14 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== -"@types/yargs@^15.0.0", "@types/yargs@^15.0.5": +"@types/yargs@^15.0.0": + version "15.0.8" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.8.tgz#7644904cad7427eb704331ea9bf1ee5499b82e23" + integrity sha512-b0BYzFUzBpOhPjpl1wtAHU994jBeKF4TKVlT7ssFv44T617XNcPdRoG4AzHLVshLzlrF7i3lTelH7UbuNYV58Q== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^15.0.5": version "15.0.5" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.5.tgz#947e9a6561483bdee9adffc983e91a6902af8b79" integrity sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w== @@ -2056,124 +2069,60 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.0.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.1.1.tgz#78d5b18e259b13c2f4ec41dd9105af269a161a75" - integrity sha512-Hoxyt99EA9LMmqo/5PuWWPeWeB3mKyvibfJ1Hy5SfiUpjE8Nqp+5QNd9fOkzL66+fqvIWSIE+Ett16LGMzCGnQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.4.0.tgz#0321684dd2b902c89128405cf0385e9fe8561934" + integrity sha512-RVt5wU9H/2H+N/ZrCasTXdGbUTkbf7Hfi9eLiA8vPQkzUJ/bLDCC3CsoZioPrNcnoyN8r0gT153dC++A4hKBQQ== dependencies: - "@typescript-eslint/experimental-utils" "4.1.1" - "@typescript-eslint/scope-manager" "4.1.1" + "@typescript-eslint/experimental-utils" "4.4.0" + "@typescript-eslint/scope-manager" "4.4.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686" - integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/typescript-estree" "3.10.1" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/experimental-utils@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.1.1.tgz#52ff4e37c93113eb96385a4e6d075abece1ea72d" - integrity sha512-jzYsNciHoa4Z3c1URtmeT/bamYm8Dwfw6vuN3WHIE/BXb1iC4KveAnXDErTAZtPVxTYBaYn3n2gbt6F6D2rm1A== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.1.1" - "@typescript-eslint/types" "4.1.1" - "@typescript-eslint/typescript-estree" "4.1.1" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/experimental-utils@^2.5.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz#d3524b644cdb40eebceca67f8cf3e4cc9c8f980f" - integrity sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA== +"@typescript-eslint/experimental-utils@4.4.0", "@typescript-eslint/experimental-utils@^4.0.1": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.4.0.tgz#62a05d3f543b8fc5dec4982830618ea4d030e1a9" + integrity sha512-01+OtK/oWeSJTjQcyzDztfLF1YjvKpLFo+JZmurK/qjSRcyObpIecJ4rckDoRCSh5Etw+jKfdSzVEHevh9gJ1w== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.34.0" + "@typescript-eslint/scope-manager" "4.4.0" + "@typescript-eslint/types" "4.4.0" + "@typescript-eslint/typescript-estree" "4.4.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^3.6.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467" - integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw== - dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "3.10.1" - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/typescript-estree" "3.10.1" - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/parser@^4.0.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.1.1.tgz#324b4b35e314075adbc92bd8330cf3ef0c88cf3e" - integrity sha512-NLIhmicpKGfJbdXyQBz9j48PA6hq6e+SDOoXy7Ak6bq1ebGqbgG+fR1UIDAuay6OjQdot69c/URu2uLlsP8GQQ== +"@typescript-eslint/parser@^4.0.1", "@typescript-eslint/parser@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.4.0.tgz#65974db9a75f23b036f17b37e959b5f99b659ec0" + integrity sha512-yc14iEItCxoGb7W4Nx30FlTyGpU9r+j+n1LUK/exlq2eJeFxczrz/xFRZUk2f6yzWfK+pr1DOTyQnmDkcC4TnA== dependencies: - "@typescript-eslint/scope-manager" "4.1.1" - "@typescript-eslint/types" "4.1.1" - "@typescript-eslint/typescript-estree" "4.1.1" + "@typescript-eslint/scope-manager" "4.4.0" + "@typescript-eslint/types" "4.4.0" + "@typescript-eslint/typescript-estree" "4.4.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.1.1.tgz#bdb8526e82435f32b4ccd9dd4cec01af97b48850" - integrity sha512-0W8TTobCvIIQ2FsrYTffyZGAAFUyIbEHq5EYJb1m7Rpd005jrnOvKOo8ywCLhs/Bm17C+KsrUboBvBAARQVvyA== - dependencies: - "@typescript-eslint/types" "4.1.1" - "@typescript-eslint/visitor-keys" "4.1.1" - -"@typescript-eslint/types@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" - integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== - -"@typescript-eslint/types@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.1.1.tgz#57500c4a86b28cb47094c1a62f1177ea279a09cb" - integrity sha512-zrBiqOKYerMTllKcn+BP+i1b7LW/EbMMYytroXMxUTvFPn1smkCu0D7lSAx29fTUO4jnwV0ljSvYQtn2vNrNxA== - -"@typescript-eslint/typescript-estree@2.34.0": - version "2.34.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz#14aeb6353b39ef0732cc7f1b8285294937cf37d5" - integrity sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg== +"@typescript-eslint/scope-manager@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.4.0.tgz#2f3dd27692a12cc9a046a90ba6a9d8cb7731190a" + integrity sha512-r2FIeeU1lmW4K3CxgOAt8djI5c6Q/5ULAgdVo9AF3hPMpu0B14WznBAtxrmB/qFVbVIB6fSx2a+EVXuhSVMEyA== dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" + "@typescript-eslint/types" "4.4.0" + "@typescript-eslint/visitor-keys" "4.4.0" -"@typescript-eslint/typescript-estree@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" - integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w== - dependencies: - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/visitor-keys" "3.10.1" - debug "^4.1.1" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" +"@typescript-eslint/types@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.4.0.tgz#63440ef87a54da7399a13bdd4b82060776e9e621" + integrity sha512-nU0VUpzanFw3jjX+50OTQy6MehVvf8pkqFcURPAE06xFNFenMj1GPEI6IESvp7UOHAnq+n/brMirZdR+7rCrlA== -"@typescript-eslint/typescript-estree@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.1.1.tgz#2015a84d71303ecdb6f46efd807ac19a51aab490" - integrity sha512-2AUg5v0liVBsqbGxBphbJ0QbGqSRVaF5qPoTPWcxop+66vMdU1h4CCvHxTC47+Qb+Pr4l2RhXDd41JNpwcQEKw== +"@typescript-eslint/typescript-estree@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.4.0.tgz#16a2df7c16710ddd5406b32b86b9c1124b1ca526" + integrity sha512-Fh85feshKXwki4nZ1uhCJHmqKJqCMba+8ZicQIhNi5d5jSQFteWiGeF96DTjO8br7fn+prTP+t3Cz/a/3yOKqw== dependencies: - "@typescript-eslint/types" "4.1.1" - "@typescript-eslint/visitor-keys" "4.1.1" + "@typescript-eslint/types" "4.4.0" + "@typescript-eslint/visitor-keys" "4.4.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" @@ -2181,19 +2130,12 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" - integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ== - dependencies: - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/visitor-keys@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.1.1.tgz#bb05664bf4bea28dc120d1da94f3027d42ab0f6f" - integrity sha512-/EOOXbA2ferGLG6RmCHEQ0lTTLkOlXYDgblCmQk3tIU7mTPLm4gKhFMeeUSe+bcchTUsKeCk8xcpbop5Zr/8Rw== +"@typescript-eslint/visitor-keys@4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.4.0.tgz#0a9118344082f14c0f051342a74b42dfdb012640" + integrity sha512-oBWeroUZCVsHLiWRdcTXJB7s1nB3taFY8WGvS23tiAlT6jXVvsdAV4rs581bgdEjOhn43q6ro7NkOiLKu6kFqA== dependencies: - "@typescript-eslint/types" "4.1.1" + "@typescript-eslint/types" "4.4.0" eslint-visitor-keys "^2.0.0" "@webassemblyjs/ast@1.9.0": @@ -2352,9 +2294,9 @@ integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== abab@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.4.tgz#6dfa57b417ca06d21b2478f0e638302f99c2405c" - integrity sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ== + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== abbrev@1: version "1.1.1" @@ -2400,9 +2342,9 @@ acorn@^6.4.1: integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== acorn@^7.1.1, acorn@^7.4.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" - integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== add@^2.0.6: version "2.0.6" @@ -2447,7 +2389,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.4: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.5" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" integrity sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag== @@ -2457,16 +2399,6 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.12.3: - version "6.12.4" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" - integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== - 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" - alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -2544,11 +2476,10 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" - integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: - "@types/color-name" "^1.1.1" color-convert "^2.0.1" anymatch@^2.0.0: @@ -3095,16 +3026,16 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-jest@^26.1.0, babel-jest@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.3.0.tgz#10d0ca4b529ca3e7d1417855ef7d7bd6fc0c3463" - integrity sha512-sxPnQGEyHAOPF8NcUsD0g7hDCnvLL2XyblRBcgrzTWBB/mAIpWow3n1bEL+VghnnZfreLhFSBsFluRoK2tRK4g== +babel-jest@^26.1.0, babel-jest@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.5.2.tgz#164f367a35946c6cf54eaccde8762dec50422250" + integrity sha512-U3KvymF3SczA3vOL/cgiUFOznfMET+XDIXiWnoJV45siAp2pLMG8i2+/MGZlAC3f/F6Q40LR4M4qDrWZ9wkK8A== dependencies: - "@jest/transform" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/transform" "^26.5.2" + "@jest/types" "^26.5.2" "@types/babel__core" "^7.1.7" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.3.0" + babel-preset-jest "^26.5.0" chalk "^4.0.0" graceful-fs "^4.2.4" slash "^3.0.0" @@ -3157,10 +3088,10 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^26.2.0: - version "26.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.2.0.tgz#bdd0011df0d3d513e5e95f76bd53b51147aca2dd" - integrity sha512-B/hVMRv8Nh1sQ1a3EY8I0n4Y1Wty3NrR5ebOyVT302op+DOAau+xNEImGMsUWOC3++ZlMooCytKz+NgN8aKGbA== +babel-plugin-jest-hoist@^26.5.0: + version "26.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.5.0.tgz#3916b3a28129c29528de91e5784a44680db46385" + integrity sha512-ck17uZFD3CDfuwCLATWZxkkuGGFhMij8quP8CNhwj8ek1mqFgbFzRJ30xwC04LLscj/aKsVFfRST+b5PT7rSuw== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -3536,9 +3467,9 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-types "^6.24.1" babel-preset-current-node-syntax@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz#b4b547acddbf963cba555ba9f9cbbb70bfd044da" - integrity sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ== + version "0.1.4" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.4.tgz#826f1f8e7245ad534714ba001f84f7e906c3b615" + integrity sha512-5/INNCYhUGqw7VbVjT/hb3ucjgkVHKXY7lX3ZjlN4gm565VyFmJUrJ/h+h16ECVB38R/9SF6aACydpKMLZ/c9w== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" @@ -3595,12 +3526,12 @@ babel-preset-flow@^6.23.0: dependencies: babel-plugin-transform-flow-strip-types "^6.22.0" -babel-preset-jest@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.3.0.tgz#ed6344506225c065fd8a0b53e191986f74890776" - integrity sha512-5WPdf7nyYi2/eRxCbVrE1kKCWxgWY4RsPEbdJWFm7QsesFGqjdkyLeu1zRkwM1cxK6EPIlNd6d2AxLk7J+t4pw== +babel-preset-jest@^26.5.0: + version "26.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.5.0.tgz#f1b166045cd21437d1188d29f7fba470d5bdb0e7" + integrity sha512-F2vTluljhqkiGSJGBg/jOruA8vIIIL11YrxRcO7nviNTMbbofPSHwnm8mgP7d/wS7wRSexRoI6X1A6T74d4LQA== dependencies: - babel-plugin-jest-hoist "^26.2.0" + babel-plugin-jest-hoist "^26.5.0" babel-preset-current-node-syntax "^0.1.3" babel-preset-react@^6.24.1: @@ -3852,7 +3783,7 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -boolean@^3.0.0, boolean@^3.0.1: +boolean@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.1.tgz#35ecf2b4a2ee191b0b44986f14eb5f052a5cbb4f" integrity sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA== @@ -3998,7 +3929,7 @@ browserslist@^3.2.6: caniuse-lite "^1.0.30000844" electron-to-chromium "^1.3.47" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5: +browserslist@^4.0.0, browserslist@^4.8.5: version "4.14.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.3.tgz#381f9e7f13794b2eb17e1761b4f118e8ae665a53" integrity sha512-GcZPC5+YqyPO4SFnz48/B0YaCwS47Q9iPChRGi6t7HhflKBcINzFrJvRfC+jp30sRMKxF+d4EHGs27Z0XP1NaQ== @@ -4008,6 +3939,16 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5: escalade "^3.1.0" node-releases "^1.1.61" +browserslist@^4.12.0: + version "4.14.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.5.tgz#1c751461a102ddc60e40993639b709be7f2c4015" + integrity sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA== + dependencies: + caniuse-lite "^1.0.30001135" + electron-to-chromium "^1.3.571" + escalade "^3.1.0" + node-releases "^1.1.61" + browserslist@^4.12.2: version "4.14.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.0.tgz#2908951abfe4ec98737b72f34c3bcedc8d43b000" @@ -4156,6 +4097,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz#049fdc59dffdd4fc285e8f4f82936591bd59fec3" + integrity sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w== + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -4169,6 +4115,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^2.0.0" + call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" @@ -4251,11 +4210,16 @@ caniuse-db@^1.0.30001090: resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30001118.tgz#60f56de236d7b68859d9a377a7f2f69092283247" integrity sha512-yMoV0unAwrdNkqKbNI4jlyUHrLsBYrzf9IOYTNxhy3t1onqpLK2nH0IPHzr2isrmmY8pQ6UPqFGka/8Gc8Yt9w== -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001111, caniuse-lite@^1.0.30001131: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001111: version "1.0.30001132" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001132.tgz#309279274f10d3aa736aa91fa269fcc8d0cd7ef9" integrity sha512-zk5FXbnsmHa0Ktc/NOZJRr+ilXva+2KFJuRiQfnjkxJfV/7DYP5C27lSQF++/veCUzVWE5xecZnSBJjf6fSwJA== +caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001131, caniuse-lite@^1.0.30001135: + version "1.0.30001146" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001146.tgz#c61fcb1474520c1462913689201fb292ba6f447c" + integrity sha512-VAy5RHDfTJhpxnDdp2n40GPPLp3KqNrXz1QqFv4J64HvArKs8nuNMOWkB3ICOaBTU/Aj4rYAo/ytdQDDFF/Pug== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -4296,7 +4260,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4483,13 +4447,6 @@ cli-boxes@^2.2.0: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -4497,7 +4454,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-spinners@^2.0.0: +cli-spinners@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== @@ -4528,6 +4485,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.1.tgz#a4cb67aad45cd83d8d05128fc9f4d8fbb887e6b3" + integrity sha512-rcvHOWyGyid6I1WjT/3NatKj2kDt9OdSHSXpyLXaMWFbKpGACNW8pRhhdPUq9MWUOdwn8Rz9AVETjF4105rZZQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -4993,24 +4959,23 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" - integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ== +css-loader@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.3.0.tgz#c888af64b2a5b2e85462c72c0f4a85c7e2e0821e" + integrity sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg== dependencies: - camelcase "^5.3.1" + camelcase "^6.0.0" cssesc "^3.0.0" icss-utils "^4.1.1" - loader-utils "^1.2.3" - normalize-path "^3.0.0" + loader-utils "^2.0.0" postcss "^7.0.32" postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" + postcss-modules-local-by-default "^3.0.3" postcss-modules-scope "^2.2.0" postcss-modules-values "^3.0.0" postcss-value-parser "^4.1.0" - schema-utils "^2.7.0" - semver "^6.3.0" + schema-utils "^2.7.1" + semver "^7.3.2" css-select-base-adapter@^0.1.1: version "0.1.1" @@ -5203,6 +5168,11 @@ d3-array@^1.2.0: resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== +d3-array@^2.4.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.8.0.tgz#f76e10ad47f1f4f75f33db5fc322eb9ffde5ef23" + integrity sha512-6V272gsOeg7+9pTW1jSYOR1QE37g95I3my1hBmY+vOUNHRrk9yt4OTz/gK7PMkVAVDrYYq4mq3grTiZ8iJdNIw== + d3-collection@1: version "1.0.7" resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" @@ -5213,12 +5183,17 @@ d3-color@1: resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== +d3-ease@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.7.tgz#9a834890ef8b8ae8c558b2fe55bd57f5993b85e2" + integrity sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ== + d3-format@1: version "1.4.5" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4" integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ== -d3-interpolate@1, d3-interpolate@^1.3.0: +d3-interpolate@1, d3-interpolate@^1.1.1, d3-interpolate@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== @@ -5230,6 +5205,19 @@ d3-path@1: resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== +d3-scale@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.7.tgz#fa90324b3ea8a776422bd0472afab0b252a0945d" + integrity sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw== + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-color "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + d3-scale@^2.1.0: version "2.2.2" resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" @@ -5242,7 +5230,7 @@ d3-scale@^2.1.0: d3-time "1" d3-time-format "2" -d3-shape@^1.2.0: +d3-shape@^1.0.0, d3-shape@^1.2.0: version "1.3.7" resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== @@ -5261,6 +5249,16 @@ d3-time@1: resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== +d3-timer@^1.0.0: + version "1.0.10" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5" + integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw== + +d3-voronoi@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297" + integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg== + damerau-levenshtein@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791" @@ -5287,7 +5285,7 @@ date-fns@^2.0.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.5.1, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -5301,7 +5299,7 @@ debug@4.1.1: dependencies: ms "^2.1.1" -debug@^3.1.1, debug@^3.2.5: +debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -5334,9 +5332,9 @@ decimal.js-light@^2.4.1: integrity sha512-b3VJCbd2hwUpeRGG3Toob+CRo8W22xplipNhP3tN7TSVB/cyMX71P1vM2Xjc9H74uV6dS2hDDmo/rHq8L87Upg== decimal.js@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" - integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw== + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== decode-uri-component@^0.2.0: version "0.2.0" @@ -5350,6 +5348,13 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + dedent@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.4.0.tgz#87defd040bd4c1595d963282ec57f3c2a8525642" @@ -5419,6 +5424,11 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" + integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== + define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -5487,6 +5497,18 @@ del@^5.1.0: rimraf "^3.0.0" slash "^3.0.0" +delaunator@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-4.0.1.tgz#3d779687f57919a7a418f8ab947d3bddb6846957" + integrity sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag== + +delaunay-find@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/delaunay-find/-/delaunay-find-0.0.5.tgz#5fb37e6509da934881b4b16c08898ac89862c097" + integrity sha512-7yAJ/wmKWj3SgqjtkGqT/RCwI0HWAo5YnHMoF5nYXD8cdci+YSo23iPmgrZUNOpDxRWN91PqxUvMMr2lKpjr+w== + dependencies: + delaunator "^4.0.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -5532,7 +5554,7 @@ detect-indent@^4.0.0: dependencies: repeating "^2.0.0" -detect-libc@^1.0.3: +detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= @@ -5565,10 +5587,10 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff-sequences@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.3.0.tgz#62a59b1b29ab7fd27cef2a33ae52abe73042d0a2" - integrity sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig== +diff-sequences@^26.5.0: + version "26.5.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.5.0.tgz#ef766cf09d43ed40406611f11c6d8d9dd8b2fefd" + integrity sha512-ZXx86srb/iYy6jG71k++wBN9P9J05UNQ5hQHQd9MtMPvcqXPx/vKU69jfHV637D00Q2gSgPk2D+jSx3l1lDW/Q== diff@^4.0.1, diff@^4.0.2: version "4.0.2" @@ -5858,22 +5880,30 @@ electron-react-devtools@^0.5.3: resolved "https://registry.yarnpkg.com/electron-react-devtools/-/electron-react-devtools-0.5.3.tgz#c74edb1245dc1cfe1380b93016cd4eb588ed00b7" integrity sha1-x07bEkXcHP4TgLkwFs1OtYjtALc= -electron-rebuild@^1.10.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.11.0.tgz#e384773a9ad30fe0a6a5bbb326b779d51f668b6a" - integrity sha512-cn6AqZBQBVtaEyj5jZW1/LOezZZ22PA1HvhEP7asvYPJ8PDF4i4UFt9be4i9T7xJKiSiomXvY5Fd+dSq3FXZxA== +electron-rebuild@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-2.2.0.tgz#c7e8ab2da2d5a6d263b03bf032ff480b7341cfab" + integrity sha512-qbrCoBSmbL/f6OwfRXg5cihAJ0TwbgRKmyK7helR6XNlaoPO42ny/+4yCTXJrYa0ZhkvcdY+gZE/wu2p19gFHg== dependencies: + "@malept/cross-spawn-promise" "^1.1.0" colors "^1.3.3" debug "^4.1.1" detect-libc "^1.0.3" - fs-extra "^8.1.0" - node-abi "^2.11.0" - node-gyp "^6.0.1" - ora "^3.4.0" - spawn-rx "^3.0.0" - yargs "^14.2.0" - -electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.523, electron-to-chromium@^1.3.570: + fs-extra "^9.0.1" + got "^11.7.0" + lzma-native "^6.0.1" + node-abi "^2.19.1" + node-gyp "^7.1.0" + ora "^5.1.0" + tar "^6.0.5" + yargs "^16.0.0" + +electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.570, electron-to-chromium@^1.3.571: + version "1.3.578" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.578.tgz#e6671936f4571a874eb26e2e833aa0b2c0b776e0" + integrity sha512-z4gU6dA1CbBJsAErW5swTGAaU2TBzc2mPAonJb00zqW1rOraDo2zfBMDRvaz9cVic+0JEZiYbHWPw/fTaZlG2Q== + +electron-to-chromium@^1.3.523: version "1.3.570" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.570.tgz#3f5141cc39b4e3892a276b4889980dabf1d29c7f" integrity sha512-Y6OCoVQgFQBP5py6A/06+yWxUZHDlNr/gNDGatjH8AZqXl8X0tE4LfjLJsXGz/JmWJz8a6K7bR1k+QzZ+k//fg== @@ -5892,9 +5922,9 @@ electron-updater@^4.3.5: semver "^7.3.2" electron@^10: - version "10.1.2" - resolved "https://registry.yarnpkg.com/electron/-/electron-10.1.2.tgz#30b6fd7669f8daf08c56219a61dfa053fa2b0c70" - integrity sha512-SvN8DcKCmPZ0UcQSNAJBfaUu+LGACqtRhUn1rW0UBLHgdbbDM76L0GU5/XGQEllH5pu5bwlCZwax3srzIl+Aeg== + version "10.1.3" + resolved "https://registry.yarnpkg.com/electron/-/electron-10.1.3.tgz#7e276e373bf30078bd4cb1184850a91268dc0e6c" + integrity sha512-CR8LrlG47MdAp317SQ3vGYa2o2cIMdMSMPYH46OVitFLk35dwE9fn3VqvhUIXhCHYcNWIAPzMhkVHpkoFdKWuw== dependencies: "@electron/get" "^1.0.1" "@types/node" "^12.0.12" @@ -6021,9 +6051,9 @@ env-paths@^2.2.0: integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== enzyme-adapter-react-16@^1.15.2: - version "1.15.4" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.4.tgz#328a782365a363ecb424f99283c4833dd92c0f21" - integrity sha512-wPzxs+JaGDK2TPYzl5a9YWGce6i2SQ3Cg51ScLeyj2WotUZ8Obcq1ke/U1Y2VGpYlb9rrX2yCjzSMgtKCeAt5w== + version "1.15.5" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.5.tgz#7a6f0093d3edd2f7025b36e7fbf290695473ee04" + integrity sha512-33yUJGT1nHFQlbVI5qdo5Pfqvu/h4qPwi1o0a6ZZsjpiqq92a3HjynDhwd1IeED+Su60HDWV8mxJqkTnLYdGkw== dependencies: enzyme-adapter-utils "^1.13.1" enzyme-shallow-equal "^1.0.4" @@ -6056,10 +6086,11 @@ enzyme-shallow-equal@^1.0.1, enzyme-shallow-equal@^1.0.4: object-is "^1.1.2" enzyme-to-json@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.5.0.tgz#3d536f1e8fb50d972360014fe2bd64e6a672f7dd" - integrity sha512-clusXRsiaQhG7+wtyc4t7MU8N3zCOgf4eY9+CeSenYzKlFST4lxerfOvnWd4SNaToKhkuba+w6m242YpQOS7eA== + version "3.6.1" + resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.6.1.tgz#d60740950bc7ca6384dfe6fe405494ec5df996bc" + integrity sha512-15tXuONeq5ORoZjV/bUo2gbtZrN2IH+Z6DvL35QmZyKHgbY1ahn6wcnLd9Xv9OjiwbAXiiP8MRZwbZrCv1wYNg== dependencies: + "@types/cheerio" "^0.22.22" lodash "^4.17.15" react-is "^16.12.0" @@ -6119,7 +6150,24 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.1.1" -es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5: +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstract@^1.17.5: + version "1.17.7" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" + integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.2" + is-regex "^1.1.1" + object-inspect "^1.8.0" + object-keys "^1.1.1" + object.assign "^4.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.17.2: version "1.17.6" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== @@ -6136,21 +6184,21 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstrac string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" -es-abstract@^1.18.0-next.0: - version "1.18.0-next.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc" - integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ== +es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1: + version "1.18.0-next.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" + integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== dependencies: es-to-primitive "^1.2.1" function-bind "^1.1.1" has "^1.0.3" has-symbols "^1.0.1" - is-callable "^1.2.0" + is-callable "^1.2.2" is-negative-zero "^2.0.0" is-regex "^1.1.1" object-inspect "^1.8.0" object-keys "^1.1.1" - object.assign "^4.1.0" + object.assign "^4.1.1" string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" @@ -6219,12 +6267,12 @@ eslint-config-airbnb-base@^14.2.0: object.assign "^4.1.0" object.entries "^1.1.2" -eslint-config-airbnb-typescript@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-9.0.0.tgz#2524f3fa6fceb3df4ae191d1e1114a04fe54c6e6" - integrity sha512-BxckAZU4rwfOidZVucAO120fTSGQAugimS8HFp7OoiordpyNkq5bxSlTPZ2XxSY8Q2NWDIygqtJKqupZld/TXA== +eslint-config-airbnb-typescript@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-11.0.0.tgz#c5a95f6b4b3be4f8791d8769b21f6cede340e428" + integrity sha512-9PtEcJow7ghv+NuqCg1huVB6X28QNxjGUeB7scIKXdBqY/z7IGg6EwMq0XJCKY5cZlOVLH+0WmKoC2HB9MszJA== dependencies: - "@typescript-eslint/parser" "^3.6.1" + "@typescript-eslint/parser" "^4.2.0" eslint-config-airbnb "^18.2.0" eslint-config-airbnb-base "^14.2.0" @@ -6238,13 +6286,13 @@ eslint-config-airbnb@^18.2.0: object.entries "^1.1.2" eslint-config-prettier@^6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" - integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== + version "6.12.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.12.0.tgz#9eb2bccff727db1c52104f0b49e87ea46605a0d2" + integrity sha512-9jWPlFlgNwRUYVoujvWTQ1aMO8o6648r+K7qU7K5Jmkbyqav1fuEZC0COYpGBxyiAJb65Ra9hrmFx19xRGwXWw== dependencies: get-stdin "^6.0.0" -eslint-import-resolver-node@^0.3.3: +eslint-import-resolver-node@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== @@ -6252,10 +6300,10 @@ eslint-import-resolver-node@^0.3.3: debug "^2.6.9" resolve "^1.13.1" -eslint-import-resolver-webpack@^0.12.2: - version "0.12.2" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.12.2.tgz#769e86cd0c752a1536c19855ebd90aa14ce384ee" - integrity sha512-7Jnm4YAoNNkvqPaZkKdIHsKGmv8/uNnYC5QsXkiSodvX4XEEfH2AKOna98FK52fCDXm3q4HzuX+7pRMKkJ64EQ== +eslint-import-resolver-webpack@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.0.tgz#5cb19cf4b6996c8a2514aeb10f909e2c70488dc3" + integrity sha512-hZWGcmjaJZK/WSCYGI/y4+FMGQZT+cwW/1E/P4rDwFj2PbanlQHISViw4ccDJ+2wxAqjgwBfxwy3seABbVKDEw== dependencies: array-find "^1.0.0" debug "^2.6.9" @@ -6291,16 +6339,16 @@ eslint-plugin-compat@^3.8.0: semver "7.3.2" eslint-plugin-import@^2.22.0: - version "2.22.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e" - integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg== + version "2.22.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" + integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw== dependencies: array-includes "^3.1.1" array.prototype.flat "^1.2.3" contains-path "^0.1.0" debug "^2.6.9" doctrine "1.5.0" - eslint-import-resolver-node "^0.3.3" + eslint-import-resolver-node "^0.3.4" eslint-module-utils "^2.6.0" has "^1.0.3" minimatch "^3.0.4" @@ -6309,12 +6357,12 @@ eslint-plugin-import@^2.22.0: resolve "^1.17.0" tsconfig-paths "^3.9.0" -eslint-plugin-jest@^23.18.0: - version "23.20.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.20.0.tgz#e1d69c75f639e99d836642453c4e75ed22da4099" - integrity sha512-+6BGQt85OREevBDWCvhqj1yYA4+BFK4XnRZSGJionuEYmcglMZYLNNBBemwzbqUAckURaHdJSBcjHPyrtypZOw== +eslint-plugin-jest@^24.1.0: + version "24.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.1.0.tgz#6708037d7602e5288ce877fd0103f329dc978361" + integrity sha512-827YJ+E8B9PvXu/0eiVSNFfxxndbKv+qE/3GSMhdorCaeaOehtqHGX2YDW9B85TEOre9n/zscledkFW/KbnyGg== dependencies: - "@typescript-eslint/experimental-utils" "^2.5.0" + "@typescript-eslint/experimental-utils" "^4.0.1" eslint-plugin-jsx-a11y@6.3.1: version "6.3.1" @@ -6351,15 +6399,15 @@ eslint-plugin-react-hooks@^4.0.8: integrity sha512-ykUeqkGyUGgwTtk78C0o8UG2fzwmgJ0qxBGPp2WqRKsTwcLuVf01kTDRAtOsd4u6whX2XOC8749n2vPydP82fg== eslint-plugin-react@^7.20.3: - version "7.20.6" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.20.6.tgz#4d7845311a93c463493ccfa0a19c9c5d0fd69f60" - integrity sha512-kidMTE5HAEBSLu23CUDvj8dc3LdBU0ri1scwHBZjI41oDv4tjsWZKU7MQccFzH1QYPYhsnTF2ovh7JlcIcmxgg== + version "7.21.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.21.4.tgz#31060b2e5ff82b12e24a3cc33edb7d12f904775c" + integrity sha512-uHeQ8A0hg0ltNDXFu3qSfFqTNPXm1XithH6/SY318UX76CMj7Q599qWpgmMhVQyvhq36pm7qvoN3pb6/3jsTFg== dependencies: array-includes "^3.1.1" array.prototype.flatmap "^1.2.3" doctrine "^2.1.0" has "^1.0.3" - jsx-ast-utils "^2.4.1" + jsx-ast-utils "^2.4.1 || ^3.0.0" object.entries "^1.1.2" object.fromentries "^2.0.2" object.values "^1.1.1" @@ -6380,7 +6428,7 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^5.0.0, eslint-scope@^5.1.0: +eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -6406,9 +6454,9 @@ eslint-visitor-keys@^2.0.0: integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== eslint@^7.9.0: - version "7.9.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.9.0.tgz#522aeccc5c3a19017cf0cb46ebfd660a79acf337" - integrity sha512-V6QyhX21+uXp4T+3nrNfI3hQNBDa/P8ga7LoQOenwrlEFXrEnUEE+ok1dMtaS3b6rmLXhT1TkTIsG75HMLbknA== + version "7.11.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.11.0.tgz#aaf2d23a0b5f1d652a08edacea0c19f7fadc0b3b" + integrity sha512-G9+qtYVCHaDi1ZuWzBsOWo2wSwd70TXnU6UHA3cTYHp7gCTXZcpggWFoUVAMRarg68qtPoNfFbzPh+VdOgmwmw== dependencies: "@babel/code-frame" "^7.0.0" "@eslint/eslintrc" "^0.1.3" @@ -6418,9 +6466,9 @@ eslint@^7.9.0: debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" - eslint-scope "^5.1.0" + eslint-scope "^5.1.1" eslint-utils "^2.1.0" - eslint-visitor-keys "^1.3.0" + eslint-visitor-keys "^2.0.0" espree "^7.3.0" esquery "^1.2.0" esutils "^2.0.2" @@ -6614,16 +6662,16 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.4.2.tgz#36db120928a5a2d7d9736643032de32f24e1b2a1" - integrity sha512-IlJ3X52Z0lDHm7gjEp+m76uX46ldH5VpqmU0006vqDju/285twh7zaWMRhs67VpQhBwjjMchk+p5aA0VkERCAA== +expect@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.5.2.tgz#3e0631c4a657a83dbec769ad246a2998953a55a6" + integrity sha512-ccTGrXZd8DZCcvCz4htGXTkd/LOoy6OEtiDS38x3/VVf6E4AQL0QoeksBiw7BtGR5xDNiRYPB8GN6pfbuTOi7w== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" ansi-styles "^4.0.0" jest-get-type "^26.3.0" - jest-matcher-utils "^26.4.2" - jest-message-util "^26.3.0" + jest-matcher-utils "^26.5.2" + jest-message-util "^26.5.2" jest-regex-util "^26.0.0" express@^4.16.3, express@^4.17.1: @@ -6840,12 +6888,12 @@ file-entry-cache@^5.0.1: flat-cache "^2.0.1" file-loader@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.1.0.tgz#65b9fcfb0ea7f65a234a1f10cdd7f1ab9a33f253" - integrity sha512-26qPdHyTsArQ6gU4P1HJbAbnFTyT2r0pG7czh1GFAd9TZbj0n94wWbupgixZH/ET/meqi2/5+F7DhW4OAXD+Lg== + version "6.1.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.1.1.tgz#a6f29dfb3f5933a1c350b2dbaa20ac5be0539baa" + integrity sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw== dependencies: loader-utils "^2.0.0" - schema-utils "^2.7.1" + schema-utils "^3.0.0" file-uri-to-path@1.0.0: version "1.0.0" @@ -7152,7 +7200,7 @@ gensync@^1.0.0-beta.1: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -7233,7 +7281,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -7411,6 +7459,23 @@ google-protobuf@3.11.4: resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.11.4.tgz#598ca405a3cfa917a2132994d008b5932ef42014" integrity sha512-lL6b04rDirurUBOgsY2+LalI6Evq8eH5TcNzi7TYQ3BsIWelT0KSOQSBsXuavEkNf+odQU6c0lgz3UsZXeNX9Q== +got@^11.7.0: + version "11.7.0" + resolved "https://registry.yarnpkg.com/got/-/got-11.7.0.tgz#a386360305571a74548872e674932b4ef70d3b24" + integrity sha512-7en2XwH2MEqOsrK0xaKhbWibBoZqy+f1RSUoIeF1BLcnf+pyQdDsljWMfmOh+QKJwuvDIiKx38GtPh5wFdGGjg== + dependencies: + "@sindresorhus/is" "^3.1.1" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.1" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -7433,7 +7498,7 @@ graceful-fs@4.1.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.4.tgz#ef089d2880f033b011823ce5c8fae798da775dbd" integrity sha1-7widKIDwM7ARgjzlyPrnmNp3Xb0= -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -7587,7 +7652,7 @@ highlight-es@^1.0.0: is-es2016-keyword "^1.0.0" js-tokens "^3.0.0" -history@^4.7.2, history@^4.9.0: +history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== @@ -7599,6 +7664,13 @@ history@^4.7.2, history@^4.9.0: tiny-warning "^1.0.0" value-equal "^1.0.1" +history@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/history/-/history-5.0.0.tgz#0cabbb6c4bbf835addb874f8259f6d25101efd08" + integrity sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg== + dependencies: + "@babel/runtime" "^7.7.6" + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -7788,6 +7860,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.0-beta.5.2" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3" + integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -7819,7 +7899,7 @@ hyphenate-style-name@^1.0.3: resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -7864,6 +7944,13 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + ignore@^4.0.3, ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -8113,10 +8200,10 @@ is-buffer@^2.0.0: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== -is-callable@^1.1.4, is-callable@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.1.tgz#4d1e21a4f437509d25ce55f8184350771421c96d" - integrity sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg== +is-callable@^1.1.4, is-callable@^1.2.0, is-callable@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" + integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== is-callable@^1.1.5: version "1.2.0" @@ -8294,6 +8381,11 @@ is-installed-globally@^0.3.1: global-dirs "^2.0.1" is-path-inside "^3.0.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-jquery-obj@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-jquery-obj/-/is-jquery-obj-0.1.1.tgz#e8d9cc9737b1ab0733b50303e33a38ed7cc2f60b" @@ -8600,57 +8692,57 @@ jake@^10.6.1: filelist "^1.0.1" minimatch "^3.0.4" -jest-changed-files@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.3.0.tgz#68fb2a7eb125f50839dab1f5a17db3607fe195b1" - integrity sha512-1C4R4nijgPltX6fugKxM4oQ18zimS7LqQ+zTTY8lMCMFPrxqBFb7KJH0Z2fRQJvw2Slbaipsqq7s1mgX5Iot+g== +jest-changed-files@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.5.2.tgz#330232c6a5c09a7f040a5870e8f0a9c6abcdbed5" + integrity sha512-qSmssmiIdvM5BWVtyK/nqVpN3spR5YyvkvPqz1x3BR1bwIxsWmU/MGwLoCrPNLbkG2ASAKfvmJpOduEApBPh2w== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" execa "^4.0.0" throat "^5.0.0" -jest-cli@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.4.2.tgz#24afc6e4dfc25cde4c7ec4226fb7db5f157c21da" - integrity sha512-zb+lGd/SfrPvoRSC/0LWdaWCnscXc1mGYW//NP4/tmBvRPT3VntZ2jtKUONsRi59zc5JqmsSajA9ewJKFYp8Cw== +jest-cli@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.5.2.tgz#0df114399b4036a3f046f0a9f25c50372c76b3a2" + integrity sha512-usm48COuUvRp8YEG5OWOaxbSM0my7eHn3QeBWxiGUuFhvkGVBvl1fic4UjC02EAEQtDv8KrNQUXdQTV6ZZBsoA== dependencies: - "@jest/core" "^26.4.2" - "@jest/test-result" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/core" "^26.5.2" + "@jest/test-result" "^26.5.2" + "@jest/types" "^26.5.2" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^26.4.2" - jest-util "^26.3.0" - jest-validate "^26.4.2" + jest-config "^26.5.2" + jest-util "^26.5.2" + jest-validate "^26.5.2" prompts "^2.0.1" - yargs "^15.3.1" + yargs "^15.4.1" -jest-config@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.4.2.tgz#da0cbb7dc2c131ffe831f0f7f2a36256e6086558" - integrity sha512-QBf7YGLuToiM8PmTnJEdRxyYy3mHWLh24LJZKVdXZ2PNdizSe1B/E8bVm+HYcjbEzGuVXDv/di+EzdO/6Gq80A== +jest-config@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.5.2.tgz#6e828e25f10124433dd008fbd83348636de0972a" + integrity sha512-dqJOnSegNdE5yDiuGHsjTM5gec7Z4AcAMHiW+YscbOYJAlb3LEtDSobXCq0or9EmGQI5SFmKy4T7P1FxetJOfg== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.4.2" - "@jest/types" "^26.3.0" - babel-jest "^26.3.0" + "@jest/test-sequencer" "^26.5.2" + "@jest/types" "^26.5.2" + babel-jest "^26.5.2" chalk "^4.0.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" - jest-environment-jsdom "^26.3.0" - jest-environment-node "^26.3.0" + jest-environment-jsdom "^26.5.2" + jest-environment-node "^26.5.2" jest-get-type "^26.3.0" - jest-jasmine2 "^26.4.2" + jest-jasmine2 "^26.5.2" jest-regex-util "^26.0.0" - jest-resolve "^26.4.0" - jest-util "^26.3.0" - jest-validate "^26.4.2" + jest-resolve "^26.5.2" + jest-util "^26.5.2" + jest-validate "^26.5.2" micromatch "^4.0.2" - pretty-format "^26.4.2" + pretty-format "^26.5.2" jest-diff@^25.2.1: version "25.5.0" @@ -8662,15 +8754,15 @@ jest-diff@^25.2.1: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-diff@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.4.2.tgz#a1b7b303bcc534aabdb3bd4a7caf594ac059f5aa" - integrity sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ== +jest-diff@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.5.2.tgz#8e26cb32dc598e8b8a1b9deff55316f8313c8053" + integrity sha512-HCSWDUGwsov5oTlGzrRM+UPJI/Dpqi9jzeV0fdRNi3Ch5bnoXhnyJMmVg2juv9081zLIy3HGPI5mcuGgXM2xRA== dependencies: chalk "^4.0.0" - diff-sequences "^26.3.0" + diff-sequences "^26.5.0" jest-get-type "^26.3.0" - pretty-format "^26.4.2" + pretty-format "^26.5.2" jest-docblock@^26.0.0: version "26.0.0" @@ -8679,41 +8771,41 @@ jest-docblock@^26.0.0: dependencies: detect-newline "^3.0.0" -jest-each@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.4.2.tgz#bb14f7f4304f2bb2e2b81f783f989449b8b6ffae" - integrity sha512-p15rt8r8cUcRY0Mvo1fpkOGYm7iI8S6ySxgIdfh3oOIv+gHwrHTy5VWCGOecWUhDsit4Nz8avJWdT07WLpbwDA== +jest-each@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.5.2.tgz#35e68d6906a7f826d3ca5803cfe91d17a5a34c31" + integrity sha512-w7D9FNe0m2D3yZ0Drj9CLkyF/mGhmBSULMQTypzAKR746xXnjUrK8GUJdlLTWUF6dd0ks3MtvGP7/xNFr9Aphg== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" chalk "^4.0.0" jest-get-type "^26.3.0" - jest-util "^26.3.0" - pretty-format "^26.4.2" + jest-util "^26.5.2" + pretty-format "^26.5.2" -jest-environment-jsdom@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.3.0.tgz#3b749ba0f3a78e92ba2c9ce519e16e5dd515220c" - integrity sha512-zra8He2btIMJkAzvLaiZ9QwEPGEetbxqmjEBQwhH3CA+Hhhu0jSiEJxnJMbX28TGUvPLxBt/zyaTLrOPF4yMJA== +jest-environment-jsdom@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.5.2.tgz#5feab05b828fd3e4b96bee5e0493464ddd2bb4bc" + integrity sha512-fWZPx0bluJaTQ36+PmRpvUtUlUFlGGBNyGX1SN3dLUHHMcQ4WseNEzcGGKOw4U5towXgxI4qDoI3vwR18H0RTw== dependencies: - "@jest/environment" "^26.3.0" - "@jest/fake-timers" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/environment" "^26.5.2" + "@jest/fake-timers" "^26.5.2" + "@jest/types" "^26.5.2" "@types/node" "*" - jest-mock "^26.3.0" - jest-util "^26.3.0" - jsdom "^16.2.2" - -jest-environment-node@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.3.0.tgz#56c6cfb506d1597f94ee8d717072bda7228df849" - integrity sha512-c9BvYoo+FGcMj5FunbBgtBnbR5qk3uky8PKyRVpSfe2/8+LrNQMiXX53z6q2kY+j15SkjQCOSL/6LHnCPLVHNw== - dependencies: - "@jest/environment" "^26.3.0" - "@jest/fake-timers" "^26.3.0" - "@jest/types" "^26.3.0" + jest-mock "^26.5.2" + jest-util "^26.5.2" + jsdom "^16.4.0" + +jest-environment-node@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.5.2.tgz#275a0f01b5e47447056f1541a15ed4da14acca03" + integrity sha512-YHjnDsf/GKFCYMGF1V+6HF7jhY1fcLfLNBDjhAOvFGvt6d8vXvNdJGVM7uTZ2VO/TuIyEFhPGaXMX5j3h7fsrA== + dependencies: + "@jest/environment" "^26.5.2" + "@jest/fake-timers" "^26.5.2" + "@jest/types" "^26.5.2" "@types/node" "*" - jest-mock "^26.3.0" - jest-util "^26.3.0" + jest-mock "^26.5.2" + jest-util "^26.5.2" jest-get-type@^25.2.6: version "25.2.6" @@ -8725,89 +8817,89 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-haste-map@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.3.0.tgz#c51a3b40100d53ab777bfdad382d2e7a00e5c726" - integrity sha512-DHWBpTJgJhLLGwE5Z1ZaqLTYqeODQIZpby0zMBsCU9iRFHYyhklYqP4EiG73j5dkbaAdSZhgB938mL51Q5LeZA== +jest-haste-map@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.5.2.tgz#a15008abfc502c18aa56e4919ed8c96304ceb23d" + integrity sha512-lJIAVJN3gtO3k4xy+7i2Xjtwh8CfPcH08WYjZpe9xzveDaqGw9fVNCpkYu6M525wKFVkLmyi7ku+DxCAP1lyMA== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" "@types/graceful-fs" "^4.1.2" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.4" jest-regex-util "^26.0.0" - jest-serializer "^26.3.0" - jest-util "^26.3.0" - jest-worker "^26.3.0" + jest-serializer "^26.5.0" + jest-util "^26.5.2" + jest-worker "^26.5.0" micromatch "^4.0.2" sane "^4.0.3" walker "^1.0.7" optionalDependencies: fsevents "^2.1.2" -jest-jasmine2@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.4.2.tgz#18a9d5bec30904267ac5e9797570932aec1e2257" - integrity sha512-z7H4EpCldHN1J8fNgsja58QftxBSL+JcwZmaXIvV9WKIM+x49F4GLHu/+BQh2kzRKHAgaN/E82od+8rTOBPyPA== +jest-jasmine2@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.5.2.tgz#0e33819d31b1f2aab5efd1e02ce502209c0e64a2" + integrity sha512-2J+GYcgLVPTkpmvHEj0/IDTIAuyblGNGlyGe4fLfDT2aktEPBYvoxUwFiOmDDxxzuuEAD2uxcYXr0+1Yw4tjFA== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.3.0" - "@jest/source-map" "^26.3.0" - "@jest/test-result" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/environment" "^26.5.2" + "@jest/source-map" "^26.5.0" + "@jest/test-result" "^26.5.2" + "@jest/types" "^26.5.2" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^26.4.2" + expect "^26.5.2" is-generator-fn "^2.0.0" - jest-each "^26.4.2" - jest-matcher-utils "^26.4.2" - jest-message-util "^26.3.0" - jest-runtime "^26.4.2" - jest-snapshot "^26.4.2" - jest-util "^26.3.0" - pretty-format "^26.4.2" + jest-each "^26.5.2" + jest-matcher-utils "^26.5.2" + jest-message-util "^26.5.2" + jest-runtime "^26.5.2" + jest-snapshot "^26.5.2" + jest-util "^26.5.2" + pretty-format "^26.5.2" throat "^5.0.0" -jest-leak-detector@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.4.2.tgz#c73e2fa8757bf905f6f66fb9e0070b70fa0f573f" - integrity sha512-akzGcxwxtE+9ZJZRW+M2o+nTNnmQZxrHJxX/HjgDaU5+PLmY1qnQPnMjgADPGCRPhB+Yawe1iij0REe+k/aHoA== +jest-leak-detector@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.5.2.tgz#83fcf9a4a6ef157549552cb4f32ca1d6221eea69" + integrity sha512-h7ia3dLzBFItmYERaLPEtEKxy3YlcbcRSjj0XRNJgBEyODuu+3DM2o62kvIFvs3PsaYoIIv+e+nLRI61Dj1CNw== dependencies: jest-get-type "^26.3.0" - pretty-format "^26.4.2" + pretty-format "^26.5.2" -jest-matcher-utils@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz#fa81f3693f7cb67e5fc1537317525ef3b85f4b06" - integrity sha512-KcbNqWfWUG24R7tu9WcAOKKdiXiXCbMvQYT6iodZ9k1f7065k0keUOW6XpJMMvah+hTfqkhJhRXmA3r3zMAg0Q== +jest-matcher-utils@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.5.2.tgz#6aa2c76ce8b9c33e66f8856ff3a52bab59e6c85a" + integrity sha512-W9GO9KBIC4gIArsNqDUKsLnhivaqf8MSs6ujO/JDcPIQrmY+aasewweXVET8KdrJ6ADQaUne5UzysvF/RR7JYA== dependencies: chalk "^4.0.0" - jest-diff "^26.4.2" + jest-diff "^26.5.2" jest-get-type "^26.3.0" - pretty-format "^26.4.2" + pretty-format "^26.5.2" -jest-message-util@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.3.0.tgz#3bdb538af27bb417f2d4d16557606fd082d5841a" - integrity sha512-xIavRYqr4/otGOiLxLZGj3ieMmjcNE73Ui+LdSW/Y790j5acqCsAdDiLIbzHCZMpN07JOENRWX5DcU+OQ+TjTA== +jest-message-util@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.5.2.tgz#6c4c4c46dcfbabb47cd1ba2f6351559729bc11bb" + integrity sha512-Ocp9UYZ5Jl15C5PNsoDiGEk14A4NG0zZKknpWdZGoMzJuGAkVt10e97tnEVMYpk7LnQHZOfuK2j/izLBMcuCZw== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.3.0" - "@types/stack-utils" "^1.0.1" + "@jest/types" "^26.5.2" + "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.4" micromatch "^4.0.2" slash "^3.0.0" stack-utils "^2.0.2" -jest-mock@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.3.0.tgz#ee62207c3c5ebe5f35b760e1267fee19a1cfdeba" - integrity sha512-PeaRrg8Dc6mnS35gOo/CbZovoDPKAeB1FICZiuagAgGvbWdNNyjQjkOaGUa/3N3JtpQ/Mh9P4A2D4Fv51NnP8Q== +jest-mock@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.5.2.tgz#c9302e8ef807f2bfc749ee52e65ad11166a1b6a1" + integrity sha512-9SiU4b5PtO51v0MtJwVRqeGEroH66Bnwtq4ARdNP7jNXbpT7+ByeWNAk4NeT/uHfNSVDXEXgQo1XRuwEqS6Rdw== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" "@types/node" "*" jest-pnp-resolver@^1.2.2: @@ -8820,170 +8912,171 @@ jest-regex-util@^26.0.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-resolve-dependencies@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.2.tgz#739bdb027c14befb2fe5aabbd03f7bab355f1dc5" - integrity sha512-ADHaOwqEcVc71uTfySzSowA/RdxUpCxhxa2FNLiin9vWLB1uLPad3we+JSSROq5+SrL9iYPdZZF8bdKM7XABTQ== +jest-resolve-dependencies@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.5.2.tgz#ee30b7cfea81c81bf5e195a9287d7ec07f893170" + integrity sha512-LLkc8LuRtxqOx0AtX/Npa2C4I23WcIrwUgNtHYXg4owYF/ZDQShcwBAHjYZIFR06+HpQcZ43+kCTMlQ3aDCYTg== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" jest-regex-util "^26.0.0" - jest-snapshot "^26.4.2" + jest-snapshot "^26.5.2" -jest-resolve@^26.4.0: - version "26.4.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.4.0.tgz#6dc0af7fb93e65b73fec0368ca2b76f3eb59a6d7" - integrity sha512-bn/JoZTEXRSlEx3+SfgZcJAVuTMOksYq9xe9O6s4Ekg84aKBObEaVXKOEilULRqviSLAYJldnoWV9c07kwtiCg== +jest-resolve@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.5.2.tgz#0d719144f61944a428657b755a0e5c6af4fc8602" + integrity sha512-XsPxojXGRA0CoDD7Vis59ucz2p3cQFU5C+19tz3tLEAlhYKkK77IL0cjYjikY9wXnOaBeEdm1rOgSJjbZWpcZg== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" chalk "^4.0.0" graceful-fs "^4.2.4" jest-pnp-resolver "^1.2.2" - jest-util "^26.3.0" + jest-util "^26.5.2" read-pkg-up "^7.0.1" resolve "^1.17.0" slash "^3.0.0" -jest-runner@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.4.2.tgz#c3ec5482c8edd31973bd3935df5a449a45b5b853" - integrity sha512-FgjDHeVknDjw1gRAYaoUoShe1K3XUuFMkIaXbdhEys+1O4bEJS8Avmn4lBwoMfL8O5oFTdWYKcf3tEJyyYyk8g== +jest-runner@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.5.2.tgz#4f9e6b0bb7eb4710c209a9e145b8a10894f4c19f" + integrity sha512-GKhYxtSX5+tXZsd2QwfkDqPIj5C2HqOdXLRc2x2qYqWE26OJh17xo58/fN/mLhRkO4y6o60ZVloan7Kk5YA6hg== dependencies: - "@jest/console" "^26.3.0" - "@jest/environment" "^26.3.0" - "@jest/test-result" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/console" "^26.5.2" + "@jest/environment" "^26.5.2" + "@jest/test-result" "^26.5.2" + "@jest/types" "^26.5.2" "@types/node" "*" chalk "^4.0.0" emittery "^0.7.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-config "^26.4.2" + jest-config "^26.5.2" jest-docblock "^26.0.0" - jest-haste-map "^26.3.0" - jest-leak-detector "^26.4.2" - jest-message-util "^26.3.0" - jest-resolve "^26.4.0" - jest-runtime "^26.4.2" - jest-util "^26.3.0" - jest-worker "^26.3.0" + jest-haste-map "^26.5.2" + jest-leak-detector "^26.5.2" + jest-message-util "^26.5.2" + jest-resolve "^26.5.2" + jest-runtime "^26.5.2" + jest-util "^26.5.2" + jest-worker "^26.5.0" source-map-support "^0.5.6" throat "^5.0.0" -jest-runtime@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.4.2.tgz#94ce17890353c92e4206580c73a8f0c024c33c42" - integrity sha512-4Pe7Uk5a80FnbHwSOk7ojNCJvz3Ks2CNQWT5Z7MJo4tX0jb3V/LThKvD9tKPNVNyeMH98J/nzGlcwc00R2dSHQ== - dependencies: - "@jest/console" "^26.3.0" - "@jest/environment" "^26.3.0" - "@jest/fake-timers" "^26.3.0" - "@jest/globals" "^26.4.2" - "@jest/source-map" "^26.3.0" - "@jest/test-result" "^26.3.0" - "@jest/transform" "^26.3.0" - "@jest/types" "^26.3.0" +jest-runtime@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.5.2.tgz#b72f5f79eb2fe0c46bfef4cdb9c1e01d1c69ba41" + integrity sha512-zArr4DatX/Sn0wswX/AnAuJgmwgAR5rNtrUz36HR8BfMuysHYNq5sDbYHuLC4ICyRdy5ae/KQ+sczxyS9G6Qvw== + dependencies: + "@jest/console" "^26.5.2" + "@jest/environment" "^26.5.2" + "@jest/fake-timers" "^26.5.2" + "@jest/globals" "^26.5.2" + "@jest/source-map" "^26.5.0" + "@jest/test-result" "^26.5.2" + "@jest/transform" "^26.5.2" + "@jest/types" "^26.5.2" "@types/yargs" "^15.0.0" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.4" - jest-config "^26.4.2" - jest-haste-map "^26.3.0" - jest-message-util "^26.3.0" - jest-mock "^26.3.0" + jest-config "^26.5.2" + jest-haste-map "^26.5.2" + jest-message-util "^26.5.2" + jest-mock "^26.5.2" jest-regex-util "^26.0.0" - jest-resolve "^26.4.0" - jest-snapshot "^26.4.2" - jest-util "^26.3.0" - jest-validate "^26.4.2" + jest-resolve "^26.5.2" + jest-snapshot "^26.5.2" + jest-util "^26.5.2" + jest-validate "^26.5.2" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.3.1" + yargs "^15.4.1" -jest-serializer@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.3.0.tgz#1c9d5e1b74d6e5f7e7f9627080fa205d976c33ef" - integrity sha512-IDRBQBLPlKa4flg77fqg0n/pH87tcRKwe8zxOVTWISxGpPHYkRZ1dXKyh04JOja7gppc60+soKVZ791mruVdow== +jest-serializer@^26.5.0: + version "26.5.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.5.0.tgz#f5425cc4c5f6b4b355f854b5f0f23ec6b962bc13" + integrity sha512-+h3Gf5CDRlSLdgTv7y0vPIAoLgX/SI7T4v6hy+TEXMgYbv+ztzbg5PSN6mUXAT/hXYHvZRWm+MaObVfqkhCGxA== dependencies: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.4.2.tgz#87d3ac2f2bd87ea8003602fbebd8fcb9e94104f6" - integrity sha512-N6Uub8FccKlf5SBFnL2Ri/xofbaA68Cc3MGjP/NuwgnsvWh+9hLIR/DhrxbSiKXMY9vUW5dI6EW1eHaDHqe9sg== +jest-snapshot@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.5.2.tgz#0cf7642eaf8e8d2736bd443f619959bf237f9ccf" + integrity sha512-MkXIDvEefzDubI/WaDVSRH4xnkuirP/Pz8LhAIDXcVQTmcEfwxywj5LGwBmhz+kAAIldA7XM4l96vbpzltSjqg== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" + "@types/babel__traverse" "^7.0.4" "@types/prettier" "^2.0.0" chalk "^4.0.0" - expect "^26.4.2" + expect "^26.5.2" graceful-fs "^4.2.4" - jest-diff "^26.4.2" + jest-diff "^26.5.2" jest-get-type "^26.3.0" - jest-haste-map "^26.3.0" - jest-matcher-utils "^26.4.2" - jest-message-util "^26.3.0" - jest-resolve "^26.4.0" + jest-haste-map "^26.5.2" + jest-matcher-utils "^26.5.2" + jest-message-util "^26.5.2" + jest-resolve "^26.5.2" natural-compare "^1.4.0" - pretty-format "^26.4.2" + pretty-format "^26.5.2" semver "^7.3.2" -jest-util@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.3.0.tgz#a8974b191df30e2bf523ebbfdbaeb8efca535b3e" - integrity sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw== +jest-util@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.5.2.tgz#8403f75677902cc52a1b2140f568e91f8ed4f4d7" + integrity sha512-WTL675bK+GSSAYgS8z9FWdCT2nccO1yTIplNLPlP0OD8tUk/H5IrWKMMRudIQQ0qp8bb4k+1Qa8CxGKq9qnYdg== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" "@types/node" "*" chalk "^4.0.0" graceful-fs "^4.2.4" is-ci "^2.0.0" micromatch "^4.0.2" -jest-validate@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.4.2.tgz#e871b0dfe97747133014dcf6445ee8018398f39c" - integrity sha512-blft+xDX7XXghfhY0mrsBCYhX365n8K5wNDC4XAcNKqqjEzsRUSXP44m6PL0QJEW2crxQFLLztVnJ4j7oPlQrQ== +jest-validate@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.5.2.tgz#7ea266700b64234cd1c0cee982490c5a80e9b0f0" + integrity sha512-FmJks0zY36mp6Af/5sqO6CTL9bNMU45yKCJk3hrz8d2aIqQIlN1pr9HPIwZE8blLaewOla134nt5+xAmWsx3SQ== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" camelcase "^6.0.0" chalk "^4.0.0" jest-get-type "^26.3.0" leven "^3.1.0" - pretty-format "^26.4.2" + pretty-format "^26.5.2" -jest-watcher@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.3.0.tgz#f8ef3068ddb8af160ef868400318dc4a898eed08" - integrity sha512-XnLdKmyCGJ3VoF6G/p5ohbJ04q/vv5aH9ENI+i6BL0uu9WWB6Z7Z2lhQQk0d2AVZcRGp1yW+/TsoToMhBFPRdQ== +jest-watcher@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.5.2.tgz#2957f4461007e0769d74b537379ecf6b7c696916" + integrity sha512-i3m1NtWzF+FXfJ3ljLBB/WQEp4uaNhX7QcQUWMokcifFTUQBDFyUMEwk0JkJ1kopHbx7Een3KX0Q7+9koGM/Pw== dependencies: - "@jest/test-result" "^26.3.0" - "@jest/types" "^26.3.0" + "@jest/test-result" "^26.5.2" + "@jest/types" "^26.5.2" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^26.3.0" + jest-util "^26.5.2" string-length "^4.0.1" -jest-worker@^26.2.1, jest-worker@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" - integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== +jest-worker@^26.5.0: + version "26.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.5.0.tgz#87deee86dbbc5f98d9919e0dadf2c40e3152fa30" + integrity sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^7.0.0" jest@^26.1.0: - version "26.4.2" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.4.2.tgz#7e8bfb348ec33f5459adeaffc1a25d5752d9d312" - integrity sha512-LLCjPrUh98Ik8CzW8LLVnSCfLaiY+wbK53U7VxnFSX7Q+kWC4noVeDvGWIFw0Amfq1lq2VfGm7YHWSLBV62MJw== + version "26.5.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.5.2.tgz#c6791642b331fe7abd2f993b0a74aa546f7be0fb" + integrity sha512-4HFabJVwsgDwul/7rhXJ3yFAF/aUkVIXiJWmgFxb+WMdZG39fVvOwYAs8/3r4AlFPc4m/n5sTMtuMbOL3kNtrQ== dependencies: - "@jest/core" "^26.4.2" + "@jest/core" "^26.5.2" import-local "^3.0.2" - jest-cli "^26.4.2" + jest-cli "^26.5.2" js-message@1.0.5: version "1.0.5" @@ -9020,7 +9113,7 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^16.2.2: +jsdom@^16.4.0: version "16.4.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb" integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w== @@ -9072,6 +9165,11 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -9230,6 +9328,14 @@ jsx-ast-utils@^2.4.1: array-includes "^3.1.1" object.assign "^4.1.0" +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.0.0.tgz#0f49d5093bafa4b45d3fe02147d8b40ffc6c7438" + integrity sha512-sPuicm6EPKYI/UnWpOatvg4pI50qaBo4dSOMGUPutmJ26ttedFKXr0It0XXPk4HKnQ/1X0st4eSS2w2jhFk9Ow== + dependencies: + array-includes "^3.1.1" + object.assign "^4.1.1" + jszip@^3.1.0: version "3.5.0" resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6" @@ -9247,6 +9353,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" + integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -9281,10 +9394,10 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -klona@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/klona/-/klona-1.1.2.tgz#a79e292518a5a5412ec8d097964bff1571a64db0" - integrity sha512-xf88rTeHiXk+XE2Vhi6yj8Wm3gMZrygGdKjJqN8HkV+PwF/t50/LdAKHoHpPcxFAlmQszTZ1CugrK25S7qDRLA== +klona@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" + integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== known-css-properties@^0.19.0: version "0.19.0" @@ -9471,11 +9584,6 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash.assign@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= - lodash.curry@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" @@ -9531,13 +9639,6 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - log-symbols@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" @@ -9611,6 +9712,16 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lzma-native@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/lzma-native/-/lzma-native-6.0.1.tgz#eec231d31b9f9ba5aea5afc86326669f01dedb58" + integrity sha512-O6oWF0xe1AFvOCjU8uOZBZ/lhjaMNwHfVNaqVMqmoQXlRwBcFWpCAToiZOdXcKVMdo/5s/D0a2QgA5laMErxHQ== + dependencies: + node-addon-api "^1.6.0" + node-pre-gyp "^0.11.0" + readable-stream "^2.3.5" + rimraf "^2.7.1" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -9854,12 +9965,17 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.44.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.41.0: +mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== -mime-types@^2.1.12, mime-types@^2.1.26, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: +mime-db@^1.41.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== @@ -9896,6 +10012,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + min-document@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" @@ -9916,14 +10037,14 @@ mini-create-react-context@^0.4.0: "@babel/runtime" "^7.5.5" tiny-warning "^1.0.3" -mini-css-extract-plugin@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" - integrity sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A== +mini-css-extract-plugin@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.0.0.tgz#4afb39f3d97b1b92eacb1ac45025416089f831bd" + integrity sha512-IsmrPv1nkdSUtFCDrAsuv5kg0k/27sLxfXqSz8vLjnbRKrNgoRdQrUNA4MppawvD+GHLkNP6L1P93Bw50ALkbg== dependencies: - loader-utils "^1.1.0" + loader-utils "^2.0.0" normalize-url "1.9.1" - schema-utils "^1.0.0" + schema-utils "^3.0.0" webpack-sources "^1.1.0" minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: @@ -10055,9 +10176,9 @@ moment-duration-format-commonjs@^1.0.0: integrity sha512-MVFR4hIh4jfuwSCPBEE5CCwn3refvTsxK/Yv/DpKJ6YcNnCimlVJ6DQeTJG1KVQPw1o8m3tkbHE9gVjivyv9iA== moment@^2.10.3, moment@^2.14.1: - version "2.28.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.28.0.tgz#cdfe73ce01327cee6537b0fafac2e0f21a237d75" - integrity sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw== + version "2.29.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" + integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== moo@^0.5.0: version "0.5.1" @@ -10109,6 +10230,11 @@ mustache@^2.1.1, mustache@^2.1.2, mustache@^2.2.1, mustache@^2.3.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-2.3.2.tgz#a6d4d9c3f91d13359ab889a812954f9230a3d0c5" integrity sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ== +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + nan@^2.12.1: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" @@ -10169,6 +10295,15 @@ nearley@^2.7.10: randexp "0.4.6" semver "^5.4.1" +needle@^2.2.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.2.tgz#cf1a8fce382b5a280108bba90a14993c00e4010a" + integrity sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -10184,13 +10319,18 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-abi@^2.11.0: +node-abi@^2.19.1: version "2.19.1" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.19.1.tgz#6aa32561d0a5e2fdb6810d8c25641b657a8cea85" integrity sha512-HbtmIuByq44yhAzK7b9j/FelKlHYISKQn0mtvcBrU5QBkhoCMp5bu8Hv5AI34DcKfOAcJBcOEMwLlwO62FFu9A== dependencies: semver "^5.4.1" +node-addon-api@^1.6.0: + version "1.7.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" + integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== + node-fetch@^1.0.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" @@ -10204,22 +10344,21 @@ node-forge@0.9.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== -node-gyp@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-6.1.0.tgz#64e31c61a4695ad304c1d5b82cf6b7c79cc79f3f" - integrity sha512-h4A2zDlOujeeaaTx06r4Vy+8MZ1679lU+wbCKDS4ZtvY2A37DESo37oejIw0mtmR3+rvNwts5B6Kpt1KrNYdNw== +node-gyp@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.0.tgz#cb8aed7ab772e73ad592ae0c71b0e3741099fe39" + integrity sha512-rjlHQlnl1dqiDZxZYiKqQdrjias7V+81OVR5PTzZioCBtWkNdrKy06M05HLKxy/pcKikKRCabeDRoZaEc6nIjw== dependencies: env-paths "^2.2.0" glob "^7.1.4" - graceful-fs "^4.2.2" - mkdirp "^0.5.1" - nopt "^4.0.1" + graceful-fs "^4.2.3" + nopt "^4.0.3" npmlog "^4.1.2" - request "^2.88.0" + request "^2.88.2" rimraf "^2.6.3" - semver "^5.7.1" - tar "^4.4.12" - which "^1.3.1" + semver "^7.3.2" + tar "^6.0.1" + which "^2.0.2" node-int64@^0.4.0: version "0.4.0" @@ -10281,6 +10420,22 @@ node-notifier@^8.0.0: uuid "^8.3.0" which "^2.0.2" +node-pre-gyp@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" + integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + node-releases@^1.1.60, node-releases@^1.1.61: version "1.1.61" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.61.tgz#707b0fca9ce4e11783612ba4a2fcba09047af16e" @@ -10291,7 +10446,7 @@ node-version@^1.0.0: resolved "https://registry.yarnpkg.com/node-version/-/node-version-1.2.0.tgz#34fde3ffa8e1149bd323983479dda620e1b5060d" integrity sha512-ma6oU4Sk0qOoKEAymVoTvk8EdXEobdS7m/mAGhDJ8Rouugho48crHBORAmy5BoOcv8wraPM6xumapQp5hl4iIQ== -nopt@^4.0.1: +nopt@^4.0.1, nopt@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== @@ -10351,6 +10506,13 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + npm-conf@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" @@ -10359,6 +10521,20 @@ npm-conf@^1.1.3: config-chain "^1.1.11" pify "^3.0.0" +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.8" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" + integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-normalize-package-bin "^1.0.1" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -10373,7 +10549,7 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npmlog@^4.1.2: +npmlog@^4.0.2, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -10429,7 +10605,7 @@ object-inspect@^1.7.0, object-inspect@^1.8.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== -object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2: +object-is@^1.0.1, object-is@^1.0.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== @@ -10437,6 +10613,14 @@ object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2: define-properties "^1.1.3" es-abstract "^1.17.5" +object-is@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81" + integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.18.0-next.1" + object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -10449,7 +10633,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: +object.assign@^4.1.0, object.assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== @@ -10527,7 +10711,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^2.0.0, onetime@^2.0.1: +onetime@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= @@ -10590,16 +10774,18 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -ora@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" - integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== +ora@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.1.0.tgz#b188cf8cd2d4d9b13fd25383bc3e5cba352c94f8" + integrity sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w== dependencies: - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-spinners "^2.0.0" - log-symbols "^2.2.0" - strip-ansi "^5.2.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.4.0" + is-interactive "^1.0.0" + log-symbols "^4.0.0" + mute-stream "0.0.8" + strip-ansi "^6.0.0" wcwidth "^1.0.1" original@^1.0.0: @@ -10642,6 +10828,11 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-cancelable@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" + integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== + p-each-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" @@ -11210,7 +11401,7 @@ postcss-modules-extract-imports@^2.0.0: dependencies: postcss "^7.0.5" -postcss-modules-local-by-default@^3.0.2: +postcss-modules-local-by-default@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== @@ -11382,7 +11573,7 @@ postcss-selector-parser@^3.0.0: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: +postcss-selector-parser@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== @@ -11391,6 +11582,16 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: indexes-of "^1.0.1" uniq "^1.0.1" +postcss-selector-parser@^6.0.2: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" + integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== + dependencies: + cssesc "^3.0.0" + indexes-of "^1.0.1" + uniq "^1.0.1" + util-deprecate "^1.0.2" + postcss-svgo@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" @@ -11425,7 +11626,7 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27: version "7.0.34" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.34.tgz#f2baf57c36010df7de4009940f21532c16d65c20" integrity sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw== @@ -11434,6 +11635,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21 source-map "^0.6.1" supports-color "^6.1.0" +postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.6: + version "7.0.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" + integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + postcss@^7.0.5: version "7.0.32" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" @@ -11485,12 +11695,12 @@ pretty-format@^25.2.1, pretty-format@^25.5.0: ansi-styles "^4.0.0" react-is "^16.12.0" -pretty-format@^26.4.2: - version "26.4.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237" - integrity sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA== +pretty-format@^26.5.2: + version "26.5.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.5.2.tgz#5d896acfdaa09210683d34b6dc0e6e21423cd3e1" + integrity sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og== dependencies: - "@jest/types" "^26.3.0" + "@jest/types" "^26.5.2" ansi-regex "^5.0.0" ansi-styles "^4.0.0" react-is "^16.12.0" @@ -11551,7 +11761,7 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -11704,6 +11914,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + raf@^3.4.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" @@ -11754,7 +11969,7 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.8: +rc@^1.2.7, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -11796,6 +12011,11 @@ react-dom@^16.12.0: prop-types "^15.6.2" scheduler "^0.19.1" +react-fast-compare@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-flame-graph@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/react-flame-graph/-/react-flame-graph-1.4.0.tgz#52d118cc94348f630a812fc0ec530a5b73c30cdb" @@ -11806,9 +12026,9 @@ react-flame-graph@^1.4.0: react-window "^1" react-hot-loader@^4.12.21: - version "4.12.21" - resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.12.21.tgz#332e830801fb33024b5a147d6b13417f491eb975" - integrity sha512-Ynxa6ROfWUeKWsTHxsrL2KMzujxJVPjs385lmB2t5cHUxdoRPGind9F00tOkdc1l5WBleOF4XEAMILY1KPIIDA== + version "4.13.0" + resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.13.0.tgz#c27e9408581c2a678f5316e69c061b226dc6a202" + integrity sha512-JrLlvUPqh6wIkrK2hZDfOyq/Uh/WeVEr8nc7hkn2/3Ul0sx1Kr5y4kOGNacNRoj7RhwLNcQ3Udf1KJXrqc0ZtA== dependencies: fast-levenshtein "^2.0.6" global "^4.3.0" @@ -12036,7 +12256,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -12351,7 +12571,7 @@ request-promise-native@^1.0.8: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.88.0, request@^2.88.2: +request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -12397,6 +12617,11 @@ resize-observer-polyfill@^1.5.0: resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== +resolve-alpn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.0.0.tgz#745ad60b3d6aff4b4a48e01b8c0bdc70959e0e8c" + integrity sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA== + resolve-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-1.0.0.tgz#4eaeea41ed040d1702457df64a42b2b07d246f9f" @@ -12475,13 +12700,12 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -restore-cursor@^2.0.0: +responselike@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" + lowercase-keys "^2.0.0" restore-cursor@^3.1.0: version "3.1.0" @@ -12523,7 +12747,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: +rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -12546,11 +12770,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: inherits "^2.0.1" roarr@^2.15.3: - version "2.15.3" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.3.tgz#65248a291a15af3ebfd767cbf7e44cb402d1d836" - integrity sha512-AEjYvmAhlyxOeB9OqPUzQCo3kuAkNfuDk/HqWbZdFsqDFpapkTjiw+p4svNEoRLvuqNTxqfL+s+gtD4eDgZ+CA== + version "2.15.4" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" + integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== dependencies: - boolean "^3.0.0" + boolean "^3.0.1" detect-node "^2.0.4" globalthis "^1.0.1" json-stringify-safe "^5.0.1" @@ -12582,13 +12806,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.1: - version "6.6.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" - integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== - dependencies: - tslib "^1.9.0" - rxjs@^6.5.2, rxjs@^6.6.2: version "6.6.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" @@ -12640,15 +12857,15 @@ sanitize-filename@^1.6.0, sanitize-filename@^1.6.2, sanitize-filename@^1.6.3: dependencies: truncate-utf8-bytes "^1.0.0" -sass-loader@^9.0.2: - version "9.0.3" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-9.0.3.tgz#086adcf0bfdcc9d920413e2cdc3ba3321373d547" - integrity sha512-fOwsP98ac1VMme+V3+o0HaaMHp8Q/C9P+MUazLFVi3Jl7ORGHQXL1XeRZt3zLSGZQQPC8xE42Y2WptItvGjDQg== +sass-loader@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.3.tgz#9e2f1bfdd6355f2adde4e4835d838b020bf800b0" + integrity sha512-W4+FV5oUdYy0PnC11ZoPrcAexODgDCa3ngxoy5X5qBhZYoPz9FPjb6Oox8Aa0ZYEyx34k8AQfOVuvqefOSAAUQ== dependencies: - klona "^1.1.2" + klona "^2.0.4" loader-utils "^2.0.0" neo-async "^2.6.2" - schema-utils "^2.7.0" + schema-utils "^3.0.0" semver "^7.3.2" sax@^1.2.4, sax@~1.2.4: @@ -12689,14 +12906,14 @@ schema-utils@^2.6.5, schema-utils@^2.7.1: ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^2.6.6, schema-utils@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" select-hose@^2.0.0: version "2.0.0" @@ -12785,6 +13002,13 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -13040,7 +13264,7 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" -source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -13070,7 +13294,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: +source-map@^0.7.3, source-map@~0.7.2: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -13080,15 +13304,6 @@ spawn-command@^0.0.2-1: resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= -spawn-rx@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spawn-rx/-/spawn-rx-3.0.0.tgz#1d33511e13ec26337da51d78630e08beb57a6767" - integrity sha512-dw4Ryg/KMNfkKa5ezAR5aZe9wNwPdKlnHEXtHOjVnyEDSPQyOpIPPRtcIiu7127SmtHhaCjw21yC43HliW0iIg== - dependencies: - debug "^2.5.1" - lodash.assign "^4.2.0" - rxjs "^6.3.1" - spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -13467,13 +13682,13 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -style-loader@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" - integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== +style-loader@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" + integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== dependencies: loader-utils "^2.0.0" - schema-utils "^2.6.6" + schema-utils "^3.0.0" style-search@^0.1.0: version "0.1.0" @@ -13507,9 +13722,9 @@ stylelint-config-standard@^20.0.0: stylelint-config-recommended "^3.0.0" stylelint@^13.6.1: - version "13.7.1" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.7.1.tgz#bee97ee78d778a3f1dbe3f7397b76414973e263e" - integrity sha512-qzqazcyRxrSRdmFuO0/SZOJ+LyCxYy0pwcvaOBBnl8/2VfHSMrtNIE+AnyJoyq6uKb+mt+hlgmVrvVi6G6XHfQ== + version "13.7.2" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.7.2.tgz#6f3c58eea4077680ed0ceb0d064b22b100970486" + integrity sha512-mmieorkfmO+ZA6CNDu1ic9qpt4tFvH2QUB7vqXgrMVHe5ENU69q7YDq0YUg/UHLuCsZOWhUAvcMcLzLDIERzSg== dependencies: "@stylelint/postcss-css-in-js" "^0.37.2" "@stylelint/postcss-markdown" "^0.36.1" @@ -13593,14 +13808,7 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -13674,7 +13882,7 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^4.4.12: +tar@^4: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -13687,7 +13895,7 @@ tar@^4.4.12: safe-buffer "^5.1.2" yallist "^3.0.3" -tar@^6.0.2: +tar@^6.0.1, tar@^6.0.2, tar@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== @@ -13735,22 +13943,22 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser-webpack-plugin@^3.0.7: - version "3.1.0" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-3.1.0.tgz#91e6d39571460ed240c0cf69d295bcf30ebf98cb" - integrity sha512-cjdZte66fYkZ65rQ2oJfrdCAkkhJA7YLYk5eGOcGCSGlq0ieZupRdjedSQXYknMPo2IveQL+tPdrxUkERENCFA== +terser-webpack-plugin@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" + integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== dependencies: cacache "^15.0.5" find-cache-dir "^3.3.1" - jest-worker "^26.2.1" + jest-worker "^26.5.0" p-limit "^3.0.2" - schema-utils "^2.6.6" - serialize-javascript "^4.0.0" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" source-map "^0.6.1" - terser "^4.8.0" + terser "^5.3.4" webpack-sources "^1.4.3" -terser@^4.1.2, terser@^4.8.0: +terser@^4.1.2: version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== @@ -13759,6 +13967,15 @@ terser@^4.1.2, terser@^4.8.0: source-map "~0.6.1" source-map-support "~0.5.12" +terser@^5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.4.tgz#e510e05f86e0bd87f01835c3238839193f77a60c" + integrity sha512-dxuB8KQo8Gt6OVOeLg/rxfcxdNZI/V1G6ze1czFUzPeCFWZRtvZMgSzlZZ5OYBZ4HoG607F6pFPNLekJyV+yVw== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.19" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -13808,10 +14025,10 @@ testcafe-browser-tools@2.0.13: read-file-relative "^1.2.0" which-promise "^1.0.0" -testcafe-hammerhead@17.1.18: - version "17.1.18" - resolved "https://registry.yarnpkg.com/testcafe-hammerhead/-/testcafe-hammerhead-17.1.18.tgz#1de65d6f63f7037a150f0987826f1f528df91c71" - integrity sha512-mrIMd1NsX+aRH37wb6tlGZoQMttXw0cwJn23iI4Y00oD8VwJfBz0x01Df1HVSuodjTqCEKdJHvDnyjAAsGjaAw== +testcafe-hammerhead@17.1.20: + version "17.1.20" + resolved "https://registry.yarnpkg.com/testcafe-hammerhead/-/testcafe-hammerhead-17.1.20.tgz#eed2eeb938ad1ca5e7a41c55e3e6e135bf599df2" + integrity sha512-1Z1r+cUjn0Cg3/Q8uwUYGeL+UvKPXAtYtiOqdGKM8/nXxndxc+q3KBvmBZXSUE9rAWhn3Mb3SQ8LoSw1+iQyiA== dependencies: acorn-hammerhead "^0.3.0" asar "^2.0.1" @@ -13888,9 +14105,9 @@ testcafe-reporter-xunit@^2.1.0: integrity sha1-5tZsVyzhWvJmcGrw/WELKoQd1EM= testcafe@^1.8.8: - version "1.9.3" - resolved "https://registry.yarnpkg.com/testcafe/-/testcafe-1.9.3.tgz#049b5d10127ce83993a46e35f429696ce774770b" - integrity sha512-1L5c8lHitGz/0Ta6AtA+J4e+YveV+XeoaY06mBWuVnijp56zJG4Xz1O2XNbMgprfLj7oNl96DW/MjJyW9B8QzA== + version "1.9.4" + resolved "https://registry.yarnpkg.com/testcafe/-/testcafe-1.9.4.tgz#a9fb9981e15cbbd559a93429adad527259921a9c" + integrity sha512-tiG0a47vvwuI6xle002wQitpEGG3Oi+qJOjFAgXT7rPYzcHrPRYP2rgefvCw/VE/qsGRHzyvDDYH3FbQMojDAA== dependencies: "@types/node" "^10.12.19" async-exit-hook "^1.1.2" @@ -13957,7 +14174,7 @@ testcafe@^1.8.8: source-map-support "^0.5.16" strip-bom "^2.0.0" testcafe-browser-tools "2.0.13" - testcafe-hammerhead "17.1.18" + testcafe-hammerhead "17.1.20" testcafe-legacy-api "4.0.0" testcafe-reporter-json "^2.1.0" testcafe-reporter-list "^2.1.0" @@ -14207,7 +14424,12 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1: + version "1.14.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.0.tgz#d624983f3e2c5e0b55307c3dd6c86acd737622c6" + integrity sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw== + +tslib@^1.9.0: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== @@ -14305,11 +14527,16 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.3.3, typescript@^3.9.7: +typescript@^3.3.3: version "3.9.7" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +typescript@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.3.tgz#153bbd468ef07725c1df9c77e8b453f8d36abba5" + integrity sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg== + typings-for-css-modules-loader@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/typings-for-css-modules-loader/-/typings-for-css-modules-loader-1.7.0.tgz#a9b5c5a0e19b719d616edfc72855ab47dedd00ae" @@ -14530,13 +14757,13 @@ urix@^0.1.0: integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-loader@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.0.tgz#c7d6b0d6b0fccd51ab3ffc58a78d32b8d89a7be2" - integrity sha512-IzgAAIC8wRrg6NYkFIJY09vtktQcsvU8V6HhtQj9PTefbYImzLB1hufqo4m+RyM5N3mLx5BqJKccgxJS+W3kqw== + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== dependencies: loader-utils "^2.0.0" - mime-types "^2.1.26" - schema-utils "^2.6.5" + mime-types "^2.1.27" + schema-utils "^3.0.0" url-parse-lax@^3.0.0: version "3.0.0" @@ -14580,7 +14807,7 @@ utf8-byte-length@^1.0.1: resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E= -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -14620,9 +14847,9 @@ uuid@^3.3.2, uuid@^3.4.0: integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== uuid@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" - integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== + version "8.3.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" + integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1: version "2.1.1" @@ -14694,6 +14921,306 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +victory-area@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-area/-/victory-area-35.3.0.tgz#e678091a1458833c9744038e236693b31bcd70a4" + integrity sha512-9828chzvj2O+lr+YW6m43wwzcHMr17h80yEAhQ2XOmrVQhUt/FNLY6RNMnhI0GYNN4DJgiSfCG5BEHMtbl2Sjg== + dependencies: + d3-shape "^1.2.0" + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-axis@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-axis/-/victory-axis-35.3.0.tgz#d8008ae890d7392c66907292e3f39fcb0ce6c187" + integrity sha512-jg+NYWIAQZd1OaIzf9vTlVOj75OxfiTR1noSIf/SsssVsT3BUlYooptPhW1mZX4lihgPbDJymX2FK5VsBPORbA== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-bar@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-bar/-/victory-bar-35.3.0.tgz#8281d16661f1f76995cdc8817f8b74fb5ed1987a" + integrity sha512-FR9vVdLvqGRQ882CXf4tD02zPc8GnLYCl+MB0w0okuA1xVjBXPqK6pvQ88fueyA4hGT64lZYIi1NkI9wgo7WfQ== + dependencies: + d3-shape "^1.2.0" + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-box-plot@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-box-plot/-/victory-box-plot-35.3.0.tgz#94ac2221f9d23c2f223ce13791d379500d5654c4" + integrity sha512-v1Ob7/GT0+jRL75DGzhMfUjeVZosFDE7Ptf9ncPqAkRCaXQ8ZiHJRDMcGqmsgtnrlsQqjLG+Y1WP985bujvOqA== + dependencies: + d3-array "^1.2.0" + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-brush-container@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-brush-container/-/victory-brush-container-35.3.0.tgz#06cc9518339aec738a7c52d0768e914edf50ea5c" + integrity sha512-PKYUpcjmL+M0W9LTfRF7/MXKWbIhFdA6eJ3U7aKupEuiY9gOJxjwVziwN4aNtHcFWwXJQTkqOWD6PkKigM6ZaA== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + victory-core "^35.3.0" + +victory-brush-line@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-brush-line/-/victory-brush-line-35.3.0.tgz#057a8ed0ce6ce406b87b05764f2cbd73a4e0f1df" + integrity sha512-HLpWTmEizsHwhOtwp+w0J34EGrXcl8H+IJ0MMMjiH0RJLyN3Bd8Rm4Y6be8gbfRDdOmbVVuFAye8AegOoACDwA== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + victory-core "^35.3.0" + +victory-candlestick@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-candlestick/-/victory-candlestick-35.3.0.tgz#ead10bc620b654cd06448f602028ca2dd6066e76" + integrity sha512-8nVfKxo51iwUmA2B2YiTybZSv1XQkFCrf6mdAz1cFGoDFGPEJFTNJ0fEE3p1bvVjH/RyAEB6g6lEHXTeJsCb0g== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-chart@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-chart/-/victory-chart-35.3.0.tgz#db9b231c15deec4d3e99b440716d4a91718385bf" + integrity sha512-+hMVyOfehH0pkEucK0Shw7/w27UkZtsrmMscNgI7kGT/rRDgILLsCc6PuSNQbFUDA+xm6wetz+fOd0VTSvuMYg== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + victory-axis "^35.3.0" + victory-core "^35.3.0" + victory-polar-axis "^35.3.0" + victory-shared-events "^35.3.0" + +victory-core@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-core/-/victory-core-35.3.0.tgz#d2578fc9044a1d82b4684d749c9573142a6ad06e" + integrity sha512-zurfbY6D/1sQxRhDpVItPGiiRxjySu2Dgm09kS9lNihIBE03pTEc+PBWnJZzUlDRX3leqh7eYV3OZbx0oXlmqA== + dependencies: + d3-ease "^1.0.0" + d3-interpolate "^1.1.1" + d3-scale "^1.0.0" + d3-shape "^1.2.0" + d3-timer "^1.0.0" + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + +victory-create-container@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-create-container/-/victory-create-container-35.3.0.tgz#6e98d6eb0e4673c707834a2290ea549584bb6ba9" + integrity sha512-RZcKJsj897KSZIIJhnkCMfpnwmYu5AsbePp0KZ8bQOtmZi75mqtsvehi5vQfNa0QjLyMBOx8J7EC7+3h5m+j2g== + dependencies: + lodash "^4.17.19" + victory-brush-container "^35.3.0" + victory-core "^35.3.0" + victory-cursor-container "^35.3.0" + victory-selection-container "^35.3.0" + victory-voronoi-container "^35.3.0" + victory-zoom-container "^35.3.0" + +victory-cursor-container@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-cursor-container/-/victory-cursor-container-35.3.0.tgz#4931c2b3d5d4f081a394abd586dc969764f6e740" + integrity sha512-zmWAAZwPrBP+FAS8A5p7BmEjeM82hvxbUpaCg9EgmRzTqfl5wPBf+gEsBc21tuneJ/5gzIsTzA5ueklBQ0rBMg== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-errorbar@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-errorbar/-/victory-errorbar-35.3.0.tgz#b6d440696ca5e28bc7f5fe8818d1c7eff344677f" + integrity sha512-gDx4m8HqlzmTmRDkGFl4ZUYFIy1zWE9AgC7ZAiAFy0J4VnAnoVVScALLjQO2nAu3NpVGFPikDCN0cLTHYP/c/Q== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-group@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-group/-/victory-group-35.3.0.tgz#df3de12058efbd1125f1378498f2ec994a3a8667" + integrity sha512-aulnBC9pb7xm/sNs15elTNSFBBnPBDw0xvYYggAyudvgHEHJSbmRefICIPUtTLKsIaNzjpOjZkJX1n15o2R5rg== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + victory-core "^35.3.0" + victory-shared-events "^35.3.0" + +victory-histogram@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-histogram/-/victory-histogram-35.3.0.tgz#23fb3ab15d2f0988cc99a13eed6e2311ccd50408" + integrity sha512-52yq28CpA1jbXdw5mPPM94audMagW4gnN0Hwz4Tkb3EXnZJkV/wdKstrH7O/Rcgqhbj/q2scqnRm/T+f7sSeEQ== + dependencies: + d3-array "^2.4.0" + d3-scale "^1.0.0" + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + victory-bar "^35.3.0" + victory-core "^35.3.0" + +victory-legend@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-legend/-/victory-legend-35.3.0.tgz#295eacc895b495fec4b34ff2c6b0b84fb8db2d90" + integrity sha512-TBNvURpEDug2+qavn5sQnLohxH3IY6GBtFKUXXBnvuhx3Z/AWKl34PbJpEzpmyhzUQjS9o+YTHH/IxFZZIsycg== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-line@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-line/-/victory-line-35.3.0.tgz#61c48ef5161ebcf8e4409a35d56b0fe90c258708" + integrity sha512-umLkeF9E7fLZ0iXQAUCm6FS1HTWJTHWaqunoI4zeHKSM8ub4vQA3cjk9vfWFE4WQsDL5Hg6QQhMfqQFh+NxkVg== + dependencies: + d3-shape "^1.2.0" + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-pie@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-pie/-/victory-pie-35.3.0.tgz#98d8ca2507690f72bcd279303e4018d2c8f753e4" + integrity sha512-Um6Giohc02EcXM0B7nVo9/owAJph3qMcHDmMl8jZSkWgl7jdMpe74A8o3a5pUBxoScDfefjJqKSv6qsPOsQ3TQ== + dependencies: + d3-shape "^1.0.0" + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-polar-axis@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-polar-axis/-/victory-polar-axis-35.3.0.tgz#806da0dd81c675d4bc6d1b505253ae2799a09e74" + integrity sha512-xX9+jU8tIesOMBm6m9Z3Z9IpIi/ya+fd9FSslZ4HNytEfjoheM84RffqIWHWlXE6kq9WwF5CmsPad3FpQ52kmg== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-scatter@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-scatter/-/victory-scatter-35.3.0.tgz#d92245c48f46739f2300df3e16ae979c9aa1c89e" + integrity sha512-Yd05kR+h9V16i0fSe39x5mwcH9Zxf2wiFQKWS2Riehm78VSiSQVEAEHepiX56BmjcgBAkllWzCZ4ozkFDTdlSg== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-selection-container@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-selection-container/-/victory-selection-container-35.3.0.tgz#499af55473210458e5f3d1bffcae9c262fcd8e61" + integrity sha512-BjiZ2wB45gxMOX37IJ0/fx5OxT4vyGVSix6SKNzV31htNROxFJBcRSS4rbh4f2g1vu/FSkrxl5DWJLkEWFemBA== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-shared-events@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-shared-events/-/victory-shared-events-35.3.0.tgz#35362903b0ef9afdd6ca5da722463ba583774fb9" + integrity sha512-DXH7g2xyv+IM3NHvJNhGj+Ugu0FLLpP4pm61u+Sie5gXuZ3EazgIgeU9KyyTKeghL3hIW4T0KG8FTalv2BYVyQ== + dependencies: + json-stringify-safe "^5.0.1" + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + victory-core "^35.3.0" + +victory-stack@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-stack/-/victory-stack-35.3.0.tgz#c77a19a7111352a3cf236838e3b7dec40a076213" + integrity sha512-6MIH5803saE8dDPgxtyDvrsaOSsjLpWW+jrzU/NJWziFnCyVDRErdLYHSRSAJcbpZeNrYAra5E0FEllyB2baFA== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + victory-core "^35.3.0" + victory-shared-events "^35.3.0" + +victory-tooltip@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-tooltip/-/victory-tooltip-35.3.0.tgz#cace6779c061e4f2677c9854dc8c65dac53b3277" + integrity sha512-t490Wyjm61JCQJlystnROEwioYyO+VX6ZR1T49YJzW5IBekJLLUXQdOgUGZvLuv3EW6xzeEkueBOP3v6nJ13wQ== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-voronoi-container@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-voronoi-container/-/victory-voronoi-container-35.3.0.tgz#ee1ceb699db0119ee7e623ab273ca3af5a456585" + integrity sha512-U8tDlmk+EOp6VALzdcHyF8ZGKp+0Wh27TOzUP9JOpcHCFxqOLAn+Prgp8FmOqMCBnv//014MKi1gCI7Bxk6J8Q== + dependencies: + delaunay-find "0.0.5" + lodash "^4.17.19" + prop-types "^15.5.8" + react-fast-compare "^2.0.0" + victory-core "^35.3.0" + victory-tooltip "^35.3.0" + +victory-voronoi@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-voronoi/-/victory-voronoi-35.3.0.tgz#52f98bde02cce96baa91f1c9b93bf26879399421" + integrity sha512-RvyY4++KaqZd+7CRtnc7N9C/tGi2i6barGmMQ+Ccw4xtzrZNtv5+ErCXC7DInXmgvMoXOVrAXAoDZaUzoRzN3g== + dependencies: + d3-voronoi "^1.1.2" + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory-zoom-container@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory-zoom-container/-/victory-zoom-container-35.3.0.tgz#a41f4e7aac47d51a9ccab6b0e12e5cef6d4661aa" + integrity sha512-1mmiFUodStPOD8xZtn67wnN5d7NE4bJ0bRXezWIEHw/7FKcUyjGFEH0qpcFOwiHKxeyfYqkiC4smx3AuRAHESg== + dependencies: + lodash "^4.17.19" + prop-types "^15.5.8" + victory-core "^35.3.0" + +victory@^35.3.0: + version "35.3.0" + resolved "https://registry.yarnpkg.com/victory/-/victory-35.3.0.tgz#98040c0aada32c90619867c42591cdc0f2b7c0bf" + integrity sha512-78RmzOLEtufnHEAOcFi3YiBryCwOAvd6zw6st+Tn7HMbDHeVSSd0Whgrk3EOpPU68mdrtLgf4XZH1mkcf2dVtw== + dependencies: + victory-area "^35.3.0" + victory-axis "^35.3.0" + victory-bar "^35.3.0" + victory-box-plot "^35.3.0" + victory-brush-container "^35.3.0" + victory-brush-line "^35.3.0" + victory-candlestick "^35.3.0" + victory-chart "^35.3.0" + victory-core "^35.3.0" + victory-create-container "^35.3.0" + victory-cursor-container "^35.3.0" + victory-errorbar "^35.3.0" + victory-group "^35.3.0" + victory-histogram "^35.3.0" + victory-legend "^35.3.0" + victory-line "^35.3.0" + victory-pie "^35.3.0" + victory-polar-axis "^35.3.0" + victory-scatter "^35.3.0" + victory-selection-container "^35.3.0" + victory-shared-events "^35.3.0" + victory-stack "^35.3.0" + victory-tooltip "^35.3.0" + victory-voronoi "^35.3.0" + victory-voronoi-container "^35.3.0" + victory-zoom-container "^35.3.0" + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -14862,9 +15389,9 @@ webpack-log@^2.0.0: uuid "^3.3.2" webpack-merge@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.1.4.tgz#a2c3a0c38ac2c02055c47bb1d42de1f072f1aea4" - integrity sha512-LSmRD59mxREGkCBm9PCW3AaV4doDqxykGlx1NvioEE0FgkT2GQI54Wyvg39ptkiq2T11eRVoV39udNPsQvK+QQ== + version "5.2.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.2.0.tgz#31cbcc954f8f89cd4b06ca8d97a38549f7f3f0c9" + integrity sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA== dependencies: clone-deep "^4.0.1" wildcard "^2.0.0" @@ -14945,9 +15472,9 @@ whatwg-mimetype@^2.3.0: integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== whatwg-url@^8.0.0: - version "8.2.1" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.2.1.tgz#ed73417230784b281fb2a32c3c501738b46167c3" - integrity sha512-ZmVCr6nfBeaMxEHALLEGy0LszYjpJqf6PVNQUQ1qd9Et+q7Jpygd4rGGDXgHjD8e99yLFseD69msHDM4YwPZ4A== + version "8.4.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837" + integrity sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw== dependencies: lodash.sortby "^4.7.0" tr46 "^2.0.2" @@ -15043,6 +15570,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -15111,6 +15647,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +y18n@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.2.tgz#48218df5da2731b4403115c39a1af709c873f829" + integrity sha512-CkwaeZw6dQgqgPGeTWKMXCRmMcBgETFlTml1+ZOO+q7kGst8NREJ+eWwFNPVUQ4QGdAaklbqCZHH6Zuep1RjiA== + yaku@^0.16.6: version "0.16.7" resolved "https://registry.yarnpkg.com/yaku/-/yaku-0.16.7.tgz#1d195c78aa9b5bf8479c895b9504fd4f0847984e" @@ -15139,14 +15680,6 @@ yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^15.0.1: - version "15.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3" - integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - yargs-parser@^18.1.2, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" @@ -15155,6 +15688,11 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^20.0.0: + version "20.2.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.1.tgz#28f3773c546cdd8a69ddae68116b48a5da328e77" + integrity sha512-yYsjuSkjbLMBp16eaOt7/siKTjNVjMm3SoJnIg3sEh/JsvqVVDyjRKmaJV4cl+lNIgq6QEco2i3gDebJl7/vLA== + yargs@^13.3.0, yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -15171,24 +15709,7 @@ yargs@^13.3.0, yargs@^13.3.2: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^14.2.0: - version "14.2.3" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414" - integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg== - dependencies: - cliui "^5.0.0" - decamelize "^1.2.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^15.0.1" - -yargs@^15.3.1, yargs@^15.4.1: +yargs@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== @@ -15205,6 +15726,19 @@ yargs@^15.3.1, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^16.0.0: + version "16.0.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.0.3.tgz#7a919b9e43c90f80d4a142a89795e85399a7e54c" + integrity sha512-6+nLw8xa9uK1BOEOykaiYAJVh6/CjxWXK/q9b5FpRgNslt8s22F2xMBqVIKgCRjNgGvGPBy8Vog7WN7yh4amtA== + dependencies: + cliui "^7.0.0" + escalade "^3.0.2" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.1" + yargs-parser "^20.0.0" + yarn@^1.22.10: version "1.22.10" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.10.tgz#c99daa06257c80f8fa2c3f1490724e394c26b18c"