diff --git a/ui/jest.config.js b/ui/jest.config.js new file mode 100644 index 0000000000..7548de4101 --- /dev/null +++ b/ui/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + roots: ['/src'], + testMatch: ['**/?(*.)+(spec|test).+(ts|tsx|js)'], + transform: { + '^.+\\.(ts|tsx)$': 'ts-jest', + }, + modulePathIgnorePatterns: ['generated'], +}; diff --git a/ui/package.json b/ui/package.json index 10623345ea..69eb7872cd 100644 --- a/ui/package.json +++ b/ui/package.json @@ -20,6 +20,7 @@ "react-hot-loader": "^3.1.3", "react-keyhooks": "^0.2.3", "react-router-dom": "5.2.0", + "recharts": "^2.9.0", "rxjs": "^6.6.6", "typescript": "^5.0.4", "web-vitals": "^1.0.1" @@ -27,7 +28,7 @@ "scripts": { "start": "NODE_OPTIONS=--openssl-legacy-provider webpack serve --config ./src/app/webpack.dev.js", "build": "rm -rf dist && NODE_OPTIONS=--openssl-legacy-provider webpack --config ./src/app/webpack.prod.js", - "test": "react-scripts test", + "test": "jest", "eject": "react-scripts eject", "protogen": "../hack/swagger-codegen.sh generate -i ../pkg/apiclient/rollout/rollout.swagger.json -l typescript-fetch -o src/models/rollout/generated" }, @@ -54,7 +55,7 @@ "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", "@types/classnames": "2.2.9", - "@types/jest": "^26.0.15", + "@types/jest": "^29.5.10", "@types/node": "^12.0.0", "@types/react": "^16.9.3", "@types/react-dom": "^16.9.3", @@ -64,10 +65,12 @@ "@types/uuid": "^9.0.3", "@types/react-autocomplete": "^1.8.4", "copy-webpack-plugin": "^6.3.2", + "jest": "^29.7.0", "mini-css-extract-plugin": "^1.3.9", "raw-loader": "^4.0.2", "react-scripts": "4.0.3", "sass": "^1.32.8", + "ts-jest": "^29.1.1", "ts-loader": "^8.0.17", "webpack-bundle-analyzer": "^4.4.0", "webpack-cli": "^4.5.0", diff --git a/ui/src/app/components/analysis-modal/analysis-modal.tsx b/ui/src/app/components/analysis-modal/analysis-modal.tsx new file mode 100644 index 0000000000..e5394995da --- /dev/null +++ b/ui/src/app/components/analysis-modal/analysis-modal.tsx @@ -0,0 +1,81 @@ +import * as React from 'react'; +import {Modal, Tabs} from 'antd'; +import {RolloutAnalysisRunInfo} from '../../../models/rollout/generated'; + +import MetricLabel from './metric-label/metric-label'; +import {MetricPanel, SummaryPanel} from './panels'; +import {analysisEndTime, analysisStartTime, getAdjustedMetricPhase, metricStatusLabel, metricSubstatus, transformMetrics} from './transforms'; +import {AnalysisStatus} from './types'; + +import classNames from 'classnames'; +import './styles.scss'; + +const cx = classNames; + +interface AnalysisModalProps { + analysis: RolloutAnalysisRunInfo; + analysisName: string; + images: string[]; + onClose: () => void; + open: boolean; + revision: string; +} + +export const AnalysisModal = ({analysis, analysisName, images, onClose, open, revision}: AnalysisModalProps) => { + const analysisResults = analysis.specAndStatus?.status; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const analysisStart = analysisStartTime(analysis.objectMeta?.creationTimestamp); + const analysisEnd = analysisEndTime(analysisResults?.metricResults ?? []); + + const analysisSubstatus = metricSubstatus( + (analysisResults?.phase ?? AnalysisStatus.Unknown) as AnalysisStatus, + analysisResults?.runSummary.failed ?? 0, + analysisResults?.runSummary.error ?? 0, + analysisResults?.runSummary.inconclusive ?? 0 + ); + const transformedMetrics = transformMetrics(analysis.specAndStatus); + + const adjustedAnalysisStatus = getAdjustedMetricPhase(analysis.status as AnalysisStatus); + + const tabItems = [ + { + label: , + key: 'analysis-summary', + children: ( + + ), + }, + ...Object.values(transformedMetrics) + .sort((a, b) => a.name.localeCompare(b.name)) + .map((metric) => ({ + label: , + key: metric.name, + children: ( + + ), + })), + ]; + + return ( + + + + ); +}; diff --git a/ui/src/app/components/analysis-modal/constants.ts b/ui/src/app/components/analysis-modal/constants.ts new file mode 100644 index 0000000000..c1a35ce0d5 --- /dev/null +++ b/ui/src/app/components/analysis-modal/constants.ts @@ -0,0 +1,15 @@ +import {AnalysisStatus, FunctionalStatus} from './types'; + +export const METRIC_FAILURE_LIMIT_DEFAULT = 0; +export const METRIC_INCONCLUSIVE_LIMIT_DEFAULT = 0; +export const METRIC_CONSECUTIVE_ERROR_LIMIT_DEFAULT = 4; + +export const ANALYSIS_STATUS_THEME_MAP: {[key in AnalysisStatus]: string} = { + Successful: FunctionalStatus.SUCCESS, + Error: FunctionalStatus.WARNING, + Failed: FunctionalStatus.ERROR, + Running: FunctionalStatus.IN_PROGRESS, + Pending: FunctionalStatus.INACTIVE, + Inconclusive: FunctionalStatus.WARNING, + Unknown: FunctionalStatus.INACTIVE, // added by frontend +}; diff --git a/ui/src/app/components/analysis-modal/criteria-list/criteria-list.scss b/ui/src/app/components/analysis-modal/criteria-list/criteria-list.scss new file mode 100644 index 0000000000..8427680d0c --- /dev/null +++ b/ui/src/app/components/analysis-modal/criteria-list/criteria-list.scss @@ -0,0 +1,19 @@ +@import '../theme/theme.scss'; + +.criteria-list { + margin: 0; + padding-left: 0; + list-style-type: none; +} + +.icon-pass { + color: $success-foreground; +} + +.icon-fail { + color: $error-foreground; +} + +.icon-pending { + color: $in-progress-foreground; +} diff --git a/ui/src/app/components/analysis-modal/criteria-list/criteria-list.tsx b/ui/src/app/components/analysis-modal/criteria-list/criteria-list.tsx new file mode 100644 index 0000000000..51247b6d22 --- /dev/null +++ b/ui/src/app/components/analysis-modal/criteria-list/criteria-list.tsx @@ -0,0 +1,116 @@ +import * as React from 'react'; +import {Space, Typography} from 'antd'; + +import {AnalysisStatus} from '../types'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; + +import {faCheck, faRotateRight, faXmark} from '@fortawesome/free-solid-svg-icons'; + +import classNames from 'classnames'; +import './criteria-list.scss'; + +const {Text} = Typography; + +enum CriterionStatus { + Fail = 'FAIL', + Pass = 'PASS', + InProgress = 'IN_PROGRESS', + Pending = 'PENDING', +} + +const defaultCriterionStatus = (analysisStatus: AnalysisStatus) => (analysisStatus === AnalysisStatus.Pending ? CriterionStatus.Pending : CriterionStatus.InProgress); + +const criterionLabel = (measurementLabel: string, maxAllowed: number) => (maxAllowed === 0 ? `No ${measurementLabel}.` : `Fewer than ${maxAllowed + 1} ${measurementLabel}.`); + +interface CriteriaListItemProps { + children: React.ReactNode; + showIcon: boolean; + status: CriterionStatus; +} + +const CriteriaListItem = ({children, showIcon, status}: CriteriaListItemProps) => { + let StatusIcon: React.ReactNode | null = null; + switch (status) { + case CriterionStatus.Fail: { + StatusIcon = ; + break; + } + case CriterionStatus.Pass: { + StatusIcon = ; + break; + } + case CriterionStatus.InProgress: { + StatusIcon = ; + break; + } + case CriterionStatus.Pending: + default: { + break; + } + } + + return ( +
  • + + {showIcon && <>{StatusIcon}} + {children} + +
  • + ); +}; + +interface CriteriaListProps { + analysisStatus: AnalysisStatus; + className?: string[] | string; + consecutiveErrors: number; + failures: number; + inconclusives: number; + maxConsecutiveErrors: number; + maxFailures: number; + maxInconclusives: number; + showIcons: boolean; +} + +const CriteriaList = ({ + analysisStatus, + className, + consecutiveErrors, + failures, + inconclusives, + maxConsecutiveErrors, + maxFailures, + maxInconclusives, + showIcons, +}: CriteriaListProps) => { + let failureStatus = defaultCriterionStatus(analysisStatus); + let errorStatus = defaultCriterionStatus(analysisStatus); + let inconclusiveStatus = defaultCriterionStatus(analysisStatus); + + if (analysisStatus !== AnalysisStatus.Pending && analysisStatus !== AnalysisStatus.Running) { + failureStatus = failures <= maxFailures ? CriterionStatus.Pass : CriterionStatus.Fail; + errorStatus = consecutiveErrors <= maxConsecutiveErrors ? CriterionStatus.Pass : CriterionStatus.Fail; + inconclusiveStatus = inconclusives <= maxInconclusives ? CriterionStatus.Pass : CriterionStatus.Fail; + } + + return ( +
      + {maxFailures > -1 && ( + + {criterionLabel('measurement failures', maxFailures)} + + )} + {maxConsecutiveErrors > -1 && ( + + {criterionLabel('consecutive measurement errors', maxConsecutiveErrors)} + + )} + {maxInconclusives > -1 && ( + + {criterionLabel('inconclusive measurements', maxInconclusives)} + + )} +
    + ); +}; + +export default CriteriaList; diff --git a/ui/src/app/components/analysis-modal/header/header.scss b/ui/src/app/components/analysis-modal/header/header.scss new file mode 100644 index 0000000000..36909a0e71 --- /dev/null +++ b/ui/src/app/components/analysis-modal/header/header.scss @@ -0,0 +1,7 @@ +.icon { + font-size: 14px; +} + +h4.title { + margin: 0; // antd override +} diff --git a/ui/src/app/components/analysis-modal/header/header.tsx b/ui/src/app/components/analysis-modal/header/header.tsx new file mode 100644 index 0000000000..e329daeda2 --- /dev/null +++ b/ui/src/app/components/analysis-modal/header/header.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; + +import {Space, Typography} from 'antd'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faMagnifyingGlassChart} from '@fortawesome/free-solid-svg-icons'; + +import StatusIndicator from '../status-indicator/status-indicator'; +import {AnalysisStatus, FunctionalStatus} from '../types'; + +import classNames from 'classnames/bind'; +import './header.scss'; + +const {Text, Title} = Typography; +const cx = classNames; + +interface HeaderProps { + className?: string[] | string; + status: AnalysisStatus; + substatus?: FunctionalStatus.ERROR | FunctionalStatus.WARNING; + subtitle?: string; + title: string; +} + +const Header = ({className, status, substatus, subtitle, title}: HeaderProps) => ( + + + + +
    + + {title} + + {subtitle && {subtitle}} +
    +
    +); + +export default Header; diff --git a/ui/src/app/components/analysis-modal/legend/legend.tsx b/ui/src/app/components/analysis-modal/legend/legend.tsx new file mode 100644 index 0000000000..e861c8cf75 --- /dev/null +++ b/ui/src/app/components/analysis-modal/legend/legend.tsx @@ -0,0 +1,44 @@ +import * as React from 'react'; + +import {Space, Typography} from 'antd'; + +import {AnalysisStatus} from '../types'; +import StatusIndicator from '../status-indicator/status-indicator'; + +import classNames from 'classnames'; + +const {Text} = Typography; + +interface LegendItemProps { + label: string; + status: AnalysisStatus; +} + +const LegendItem = ({label, status}: LegendItemProps) => ( + + + {label} + +); + +const pluralize = (count: number, singular: string, plural: string) => (count === 1 ? singular : plural); + +interface LegendProps { + className?: string[] | string; + errors: number; + failures: number; + inconclusives: number; + successes: number; +} + +const Legend = ({className, errors, failures, inconclusives, successes}: LegendProps) => ( + + + + + {inconclusives > 0 && } + +); + +export default Legend; +export {LegendItem}; diff --git a/ui/src/app/components/analysis-modal/metric-chart/metric-chart.scss b/ui/src/app/components/analysis-modal/metric-chart/metric-chart.scss new file mode 100644 index 0000000000..953b10e06c --- /dev/null +++ b/ui/src/app/components/analysis-modal/metric-chart/metric-chart.scss @@ -0,0 +1,79 @@ +@import '../theme/theme.scss'; + +@mixin chartDot($background, $foreground) { + fill: $background; + stroke: $foreground; +} + +.metric-chart svg { + overflow: visible; +} + +.metric-chart-tooltip { + background: white; + max-width: 350px; + padding: 8px; + box-shadow: $shadow-1; +} + +.metric-chart-tooltip-timestamp { + margin-left: 16px; +} + +.metric-chart-tooltip-status { + display: flex; + + > :first-child { + margin: 4px 4px 0 0; + } +} + +.chart-axis text, +.chart-label > tspan { + stroke: $gray-10; + font-size: 11px; + font-family: $font-family-primary; + font-weight: 100; +} + +.dot-ERROR { + @include chartDot($error-background, $error-foreground); +} +.dot-INACTIVE { + @include chartDot($inactive-background, $inactive-foreground); +} +.dot-IN_PROGRESS { + @include chartDot($in-progress-background, $in-progress-foreground); +} +.dot-SUCCESS { + @include chartDot($success-background, $success-foreground); +} +.dot-WARNING { + @include chartDot($warning-background, $warning-foreground); +} + +.chart-line > path { + stroke: $gray-11; +} + +.reference-line { + &.is-ERROR > line { + stroke: $error-foreground; + } + &.is-SUCCESS > line { + stroke: $success-foreground; + } +} + +.reference-area { + > path { + opacity: 0.3; + } + + &.is-ERROR > path { + fill: $error-background; + } + &.is-SUCCESS > path { + fill: $success-background; + } +} diff --git a/ui/src/app/components/analysis-modal/metric-chart/metric-chart.tsx b/ui/src/app/components/analysis-modal/metric-chart/metric-chart.tsx new file mode 100644 index 0000000000..f912acddbf --- /dev/null +++ b/ui/src/app/components/analysis-modal/metric-chart/metric-chart.tsx @@ -0,0 +1,161 @@ +// eslint-disable-file @typescript-eslint/ban-ts-comment +import * as React from 'react'; +import * as moment from 'moment'; +import {CartesianGrid, DotProps, Label, Line, LineChart, ReferenceLine, ResponsiveContainer, Tooltip, TooltipProps, XAxis, YAxis} from 'recharts'; +import {NameType, ValueType} from 'recharts/types/component/DefaultTooltipContent'; +import {Typography} from 'antd'; + +import {AnalysisStatus, FunctionalStatus, TransformedMeasurement} from '../types'; +import {ANALYSIS_STATUS_THEME_MAP} from '../constants'; +import {isValidDate} from '../transforms'; + +import StatusIndicator from '../status-indicator/status-indicator'; + +import classNames from 'classnames/bind'; +import './metric-chart.scss'; + +const {Text} = Typography; +const cx = classNames; + +const CHART_HEIGHT = 254; +const X_AXIS_HEIGHT = 45; + +const defaultValueFormatter = (value: number | string | null) => (value === null ? '' : value.toString()); + +const timeTickFormatter = (axisData?: string) => { + if (axisData === undefined || !isValidDate(axisData)) { + return ''; + } + return moment(axisData).format('LT'); +}; + +type MeasurementDotProps = DotProps & { + payload?: { + phase: AnalysisStatus; + startedAt: string; + value: string | null; + }; +}; + +const MeasurementDot = ({cx, cy, payload}: MeasurementDotProps) => ( + +); + +type TooltipContentProps = TooltipProps & { + conditionKeys: string[]; + valueFormatter: (value: number | string | null) => string; +}; + +const TooltipContent = ({active, conditionKeys, payload, valueFormatter}: TooltipContentProps) => { + if (!active || payload?.length === 0 || !payload?.[0].payload) { + return null; + } + + const data = payload[0].payload; + let label; + if (data.phase === AnalysisStatus.Error) { + label = data.message ?? 'Measurement error'; + } else if (conditionKeys.length > 0) { + const sublabels = conditionKeys.map((cKey) => (conditionKeys.length > 1 ? `${valueFormatter(data.chartValue[cKey])} (${cKey})` : valueFormatter(data.chartValue[cKey]))); + label = sublabels.join(' , '); + } else { + label = valueFormatter(data.chartValue); + } + + return ( +
    + + {moment(data.startedAt).format('LTS')} + +
    + + {label} +
    +
    + ); +}; + +interface MetricChartProps { + className?: string[] | string; + conditionKeys: string[]; + data: TransformedMeasurement[]; + failThresholds: number[] | null; + max: number | null; + min: number | null; + successThresholds: number[] | null; + valueFormatter?: (value: number | string | null) => string; + yAxisFormatter?: (value: any, index: number) => string; + yAxisLabel: string; +} + +const MetricChart = ({ + className, + conditionKeys, + data, + failThresholds, + max, + min, + successThresholds, + valueFormatter = defaultValueFormatter, + yAxisFormatter = defaultValueFormatter, + yAxisLabel, +}: MetricChartProps) => { + // show ticks at boundaries of analysis + // @ts-ignore + const startingTick = data[0]?.startedAt ?? ''; + // @ts-ignore + const endingTick = data[data.length - 1]?.finishedAt ?? ''; + const timeTicks: any[] = [startingTick, endingTick]; + + return ( + + + + + + + } filterNull={false} isAnimationActive={true} /> + {failThresholds !== null && ( + <> + {failThresholds.map((threshold) => ( + + ))} + + )} + {successThresholds !== null && ( + <> + {successThresholds.map((threshold) => ( + + ))} + + )} + {conditionKeys.length === 0 ? ( + } + /> + ) : ( + <> + {conditionKeys.map((cKey) => ( + } /> + ))} + + )} + + + ); +}; + +export default MetricChart; diff --git a/ui/src/app/components/analysis-modal/metric-label/metric-label.scss b/ui/src/app/components/analysis-modal/metric-label/metric-label.scss new file mode 100644 index 0000000000..33b0f965fc --- /dev/null +++ b/ui/src/app/components/analysis-modal/metric-label/metric-label.scss @@ -0,0 +1,9 @@ +.metric-label { + display: block; + width: 140px; + min-width: 140px; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/ui/src/app/components/analysis-modal/metric-label/metric-label.tsx b/ui/src/app/components/analysis-modal/metric-label/metric-label.tsx new file mode 100644 index 0000000000..5725f17cdc --- /dev/null +++ b/ui/src/app/components/analysis-modal/metric-label/metric-label.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; + +import {Space} from 'antd'; + +import {AnalysisStatus, FunctionalStatus} from '../types'; +import StatusIndicator from '../status-indicator/status-indicator'; + +import classNames from 'classnames/bind'; +import './metric-label.scss'; + +const cx = classNames; + +interface AnalysisModalProps { + label: string; + status: AnalysisStatus; + substatus?: FunctionalStatus.ERROR | FunctionalStatus.WARNING; +} + +const MetricLabel = ({label, status, substatus}: AnalysisModalProps) => ( + + + + {label} + + +); + +export default MetricLabel; diff --git a/ui/src/app/components/analysis-modal/metric-table/metric-table.scss b/ui/src/app/components/analysis-modal/metric-table/metric-table.scss new file mode 100644 index 0000000000..69a19025bb --- /dev/null +++ b/ui/src/app/components/analysis-modal/metric-table/metric-table.scss @@ -0,0 +1,32 @@ +@import '../theme/theme.scss'; + +.metric-table { + border: 1px solid $gray-4; +} + +.error-message { + font-style: italic; +} + +.condition { + display: flex; + align-items: center; + justify-content: flex-end; + margin-top: $space-unit; + font-size: 12px; + + &::before { + content: ''; + display: block; + height: 2px; + width: 12px; + margin-right: $space-unit; + } + + &.is-ERROR::before { + background: $error-foreground; + } + &.is-SUCCESS::before { + background: $success-foreground; + } +} diff --git a/ui/src/app/components/analysis-modal/metric-table/metric-table.tsx b/ui/src/app/components/analysis-modal/metric-table/metric-table.tsx new file mode 100644 index 0000000000..1fdbd1363d --- /dev/null +++ b/ui/src/app/components/analysis-modal/metric-table/metric-table.tsx @@ -0,0 +1,66 @@ +import * as React from 'react'; +import * as moment from 'moment'; +import {Table, Typography} from 'antd'; + +import {AnalysisStatus, TransformedMeasurement, TransformedValueObject} from '../types'; +import StatusIndicator from '../status-indicator/status-indicator'; +import {isValidDate} from '../transforms'; + +import classNames from 'classnames/bind'; +import './metric-table.scss'; + +const {Column} = Table; +const {Text} = Typography; + +const timeColFormatter = (startTime?: string) => (isValidDate(startTime) ? moment(startTime).format('LTS') : ''); + +const isObject = (tValue: TransformedValueObject | number | string | null) => typeof tValue === 'object' && !Array.isArray(tValue) && tValue !== null; + +const columnValueLabel = (value: any, valueKey: string) => (isObject(value) && valueKey in (value as TransformedValueObject) ? (value as TransformedValueObject)[valueKey] : ''); + +interface MetricTableProps { + className?: string[] | string; + conditionKeys: string[]; + data: TransformedMeasurement[]; + failCondition: string | null; + successCondition: string | null; +} + +const MetricTable = ({className, conditionKeys, data, failCondition, successCondition}: MetricTableProps) => ( +
    + + } align='center' /> + {conditionKeys.length > 0 ? ( + <> + {conditionKeys.map((cKey) => ( + { + const isError = columnValue.phase === AnalysisStatus.Error; + const errorMessage = columnValue.message ?? 'Measurement error'; + const label = isError ? errorMessage : columnValueLabel(columnValue.tableValue, cKey); + return {label}; + }} + /> + ))} + + ) : ( + + )} + {timeColFormatter(startedAt)}} /> +
    + {failCondition !== null && ( + + Failure condition: {failCondition} + + )} + {successCondition !== null && ( + + Success condition: {successCondition} + + )} +
    +); + +export default MetricTable; diff --git a/ui/src/app/components/analysis-modal/panels/index.tsx b/ui/src/app/components/analysis-modal/panels/index.tsx new file mode 100644 index 0000000000..5636632889 --- /dev/null +++ b/ui/src/app/components/analysis-modal/panels/index.tsx @@ -0,0 +1,2 @@ +export {default as MetricPanel} from './metric-panel'; +export {default as SummaryPanel} from './summary-panel'; diff --git a/ui/src/app/components/analysis-modal/panels/metric-panel.tsx b/ui/src/app/components/analysis-modal/panels/metric-panel.tsx new file mode 100644 index 0000000000..b1deba1c6f --- /dev/null +++ b/ui/src/app/components/analysis-modal/panels/metric-panel.tsx @@ -0,0 +1,137 @@ +// eslint-disable-file @typescript-eslint/ban-ts-comment +import * as React from 'react'; + +import {Radio, Typography} from 'antd'; +import type {RadioChangeEvent} from 'antd'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faChartLine, faList} from '@fortawesome/free-solid-svg-icons'; + +import Header from '../header/header'; +import CriteriaList from '../criteria-list/criteria-list'; +import Legend from '../legend/legend'; +import MetricChart from '../metric-chart/metric-chart'; +import MetricTable from '../metric-table/metric-table'; +import QueryBox from '../query-box/query-box'; +import {AnalysisStatus, FunctionalStatus, TransformedMetricSpec, TransformedMetricStatus} from '../types'; +import {isFiniteNumber} from '../transforms'; +import {METRIC_CONSECUTIVE_ERROR_LIMIT_DEFAULT, METRIC_FAILURE_LIMIT_DEFAULT, METRIC_INCONCLUSIVE_LIMIT_DEFAULT} from '../constants'; + +import classNames from 'classnames'; +import './styles.scss'; + +const cx = classNames; + +const {Paragraph, Title} = Typography; + +interface MetricPanelProps { + className?: string[] | string; + metricName: string; + metricSpec?: TransformedMetricSpec; + metricResults: TransformedMetricStatus; + status: AnalysisStatus; + substatus?: FunctionalStatus.ERROR | FunctionalStatus.WARNING; +} + +const MetricPanel = ({className, metricName, metricSpec, metricResults, status, substatus}: MetricPanelProps) => { + const consecutiveErrorLimit = isFiniteNumber(metricSpec.consecutiveErrorLimit ?? null) ? metricSpec.consecutiveErrorLimit : METRIC_CONSECUTIVE_ERROR_LIMIT_DEFAULT; + const failureLimit = isFiniteNumber(metricSpec.failureLimit ?? null) ? metricSpec.failureLimit : METRIC_FAILURE_LIMIT_DEFAULT; + const inconclusiveLimit = isFiniteNumber(metricSpec.inconclusiveLimit ?? null) ? metricSpec.inconclusiveLimit : METRIC_INCONCLUSIVE_LIMIT_DEFAULT; + + const canChartMetric = metricResults.chartable && metricResults.chartMax !== null; + + const [selectedView, setSelectedView] = React.useState(canChartMetric ? 'chart' : 'table'); + + const onChangeView = ({target: {value}}: RadioChangeEvent) => { + setSelectedView(value); + }; + + return ( +
    +
    +
    + {canChartMetric && ( + + + + + + + + + )} +
    + {status === AnalysisStatus.Pending && ( + + {metricName} analysis measurements have not yet begun. Measurement information will appear here when it becomes available. + + )} + {status !== AnalysisStatus.Pending && metricResults.transformedMeasurements.length === 0 && ( + Measurement results for {metricName} cannot be displayed. + )} + {status !== AnalysisStatus.Pending && metricResults.transformedMeasurements.length > 0 && ( + <> + + {selectedView === 'chart' && ( + + )} + {selectedView === 'table' && ( + + )} + + )} +
    + + Pass requirements + + 0} + /> +
    + {Array.isArray(metricSpec?.queries) && ( + <> +
    + + {metricSpec.queries.length > 1 ? 'Queries' : 'Query'} + +
    + {metricSpec.queries.map((query) => ( + + ))} + + )} +
    + ); +}; + +export default MetricPanel; diff --git a/ui/src/app/components/analysis-modal/panels/styles.scss b/ui/src/app/components/analysis-modal/panels/styles.scss new file mode 100644 index 0000000000..b0010c55dd --- /dev/null +++ b/ui/src/app/components/analysis-modal/panels/styles.scss @@ -0,0 +1,61 @@ +@import '../theme/theme.scss'; + +// Analysis Panel + +.analysis-header { + margin: $space-unit 0 (3 * $space-unit); +} + +.label { + display: block; +} + +// Metric Panel + +.metric-header { + display: flex; + align-items: center; + justify-content: space-between; + margin: $space-unit 0; +} + +.legend { + display: flex; + justify-content: flex-end; +} + +h5.section-title { + margin-bottom: 0; // antd override +} + +.query-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: $space-unit; +} + +.query-box { + :not(:last-child) { + margin-bottom: $space-small; + } + + :last-child { + margin-bottom: $space-large; + } +} + +// Common + +.summary-section, +.metric-section { + margin-bottom: 3 * $space-unit; + + &.medium-space { + margin-bottom: $space-medium; + } + + &.top-content { + margin-top: $space-unit; + } +} diff --git a/ui/src/app/components/analysis-modal/panels/summary-panel.tsx b/ui/src/app/components/analysis-modal/panels/summary-panel.tsx new file mode 100644 index 0000000000..4b2d7bb829 --- /dev/null +++ b/ui/src/app/components/analysis-modal/panels/summary-panel.tsx @@ -0,0 +1,73 @@ +import * as React from 'react'; +import * as moment from 'moment'; +import {Typography} from 'antd'; + +import {AnalysisStatus, FunctionalStatus} from '../types'; +import Header from '../header/header'; + +import classNames from 'classnames/bind'; +import './styles.scss'; + +const cx = classNames; + +const {Text} = Typography; + +const timeRangeFormatter = (start: number, end: number | null) => { + const startFormatted = moment(start).format('LLL'); + if (end === null) { + return `${startFormatted} - present`; + } + const isSameDate = moment(start).isSame(moment(end), 'day'); + const endFormatted = isSameDate ? moment(end).format('LT') : moment(end).format('LLL'); + return `${startFormatted} - ${endFormatted}`; +}; + +interface SummaryPanelProps { + className?: string[] | string; + endTime: number | null; + images: string[]; + message?: string; + revision: string; + startTime: number | null; + status: AnalysisStatus; + substatus?: FunctionalStatus.ERROR | FunctionalStatus.WARNING; + title: string; +} + +const SummaryPanel = ({className, endTime, images, message, revision, startTime, status, substatus, title}: SummaryPanelProps) => ( +
    +
    + {images.length > 0 && ( +
    + + {images.length > 1 ? `Versions` : `Version`} + + {images.join(', ')} +
    + )} +
    + + Revision + + {revision} +
    + {startTime !== null && ( +
    + + Run time + + {timeRangeFormatter(startTime, endTime)} +
    + )} + {message && ( +
    + + Summary + + {message} +
    + )} +
    +); + +export default SummaryPanel; diff --git a/ui/src/app/components/analysis-modal/query-box/query-box.scss b/ui/src/app/components/analysis-modal/query-box/query-box.scss new file mode 100644 index 0000000000..c12be636b2 --- /dev/null +++ b/ui/src/app/components/analysis-modal/query-box/query-box.scss @@ -0,0 +1,50 @@ +@import '../theme/theme.scss'; + +.query-box { + position: relative; + padding: $space-small 48px $space-small $space-small; + background-color: $gray-2; + border: 1px solid $gray-4; + max-height: 70px; + overflow: hidden; + transition: max-height 0.3s ease-in-out; + + &.can-expand { + cursor: pointer; + } + + .query { + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: auto hidden; + white-space: pre-wrap; + word-wrap: break-word; + text-overflow: ellipsis; + line-clamp: 3; + -webkit-line-clamp: 3; + font-family: $font-family-mono; + font-size: 12px; + margin-bottom: 0; + } + + &.is-expanded { + max-height: 500px; + + .query { + line-clamp: initial; + -webkit-line-clamp: initial; + } + } +} + +.query-copy-button { + position: absolute; + font-size: 18px; + line-height: 1; + top: 8px; + right: 6px; + + svg { + color: $ant-primary; + } +} diff --git a/ui/src/app/components/analysis-modal/query-box/query-box.tsx b/ui/src/app/components/analysis-modal/query-box/query-box.tsx new file mode 100644 index 0000000000..188731c5a8 --- /dev/null +++ b/ui/src/app/components/analysis-modal/query-box/query-box.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; + +import {Typography} from 'antd'; + +import classNames from 'classnames'; +import './query-box.scss'; + +const {Paragraph} = Typography; + +interface QueryBoxProps { + className?: string[] | string; + query: string; +} + +const QueryBox = ({className, query}: QueryBoxProps) => { + const queryTextRef = React.useRef(null); + const [canExpand, setCanExpand] = React.useState(false); + const [expanded, toggleExpanded] = React.useState(false); + + React.useEffect(() => { + setCanExpand(queryTextRef.current?.offsetHeight !== queryTextRef.current?.scrollHeight); + }, [queryTextRef]); + + const expandQuery = () => { + toggleExpanded(true); + setCanExpand(false); + }; + + return ( +
    +
    +                {query}
    +            
    + +
    + ); +}; + +export default QueryBox; diff --git a/ui/src/app/components/analysis-modal/status-indicator/status-indicator.scss b/ui/src/app/components/analysis-modal/status-indicator/status-indicator.scss new file mode 100644 index 0000000000..35799cf1ff --- /dev/null +++ b/ui/src/app/components/analysis-modal/status-indicator/status-indicator.scss @@ -0,0 +1,84 @@ +@import '../theme/theme.scss'; + +@mixin indicator($background, $foreground) { + background: $background; + border-color: $foreground; + + &, + svg { + color: $foreground; + } +} + +.indicator-wrapper { + position: relative; + border-radius: 50%; +} + +.indicator { + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border-style: solid; + border-width: 1px; + + &.is-small { + width: 14px; + min-width: 14px; + height: 14px; + } + + &.is-large { + width: 28px; + min-width: 28px; + height: 28px; + } + + &, + &.is-INACTIVE { + @include indicator($inactive-background, $inactive-foreground); + } + + &.is-IN_PROGRESS { + @include indicator($in-progress-background, $in-progress-foreground); + } + + &.is-SUCCESS { + @include indicator($success-background, $success-foreground); + } + + &.is-WARNING { + @include indicator($warning-background, $warning-foreground); + } + + &.is-ERROR { + @include indicator($error-background, $error-foreground); + } +} + +.substatus { + position: absolute; + border: 1px solid white; + border-radius: 50%; + top: -3px; + left: -2px; + + &.is-small { + width: 8px; + height: 8px; + } + + &.is-large { + width: 12px; + height: 12px; + } + + &.is-WARNING { + background: $warning-foreground; + } + + &.is-ERROR { + background: $error-foreground; + } +} diff --git a/ui/src/app/components/analysis-modal/status-indicator/status-indicator.tsx b/ui/src/app/components/analysis-modal/status-indicator/status-indicator.tsx new file mode 100644 index 0000000000..5dd30f8e5d --- /dev/null +++ b/ui/src/app/components/analysis-modal/status-indicator/status-indicator.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; + +import {AnalysisStatus, FunctionalStatus} from '../types'; +import {ANALYSIS_STATUS_THEME_MAP} from '../constants'; + +import classNames from 'classnames'; +import './status-indicator.scss'; + +const cx = classNames; + +interface StatusIndicatorProps { + children?: React.ReactNode; + className?: string[] | string; + size?: 'small' | 'large'; + status: AnalysisStatus; + substatus?: FunctionalStatus.ERROR | FunctionalStatus.WARNING; +} + +const StatusIndicator = ({children, className, size = 'large', status, substatus}: StatusIndicatorProps) => ( +
    +
    {children}
    + {substatus !== undefined &&
    } +
    +); + +export default StatusIndicator; diff --git a/ui/src/app/components/analysis-modal/styles.scss b/ui/src/app/components/analysis-modal/styles.scss new file mode 100644 index 0000000000..89c15c36fe --- /dev/null +++ b/ui/src/app/components/analysis-modal/styles.scss @@ -0,0 +1,8 @@ +.tabs { + min-height: 550px; + margin-top: 16px !important; // antd override + + .ant-tabs-tab { + padding-left: 0 !important; // antd override + } +} diff --git a/ui/src/app/components/analysis-modal/theme/theme.scss b/ui/src/app/components/analysis-modal/theme/theme.scss new file mode 100644 index 0000000000..0d8f934264 --- /dev/null +++ b/ui/src/app/components/analysis-modal/theme/theme.scss @@ -0,0 +1,35 @@ +@import 'node_modules/argo-ui/v2/styles/colors'; + +// antd colors +$gray-2: #fafafa; +$gray-4: #f0f0f0; +$gray-5: #d9d9d9; +$gray-6: #bfbfbf; +$gray-7: #8c8c8c; +$gray-10: #262626; +$gray-11: #1f1f1f; +$gray-12: #141414; + +$ant-primary: #44505f; // from config/theme.ts + +$success-background: lighten($argo-success-color-dark, 35); +$success-foreground: $argo-success-color-dark; +$warning-background: lighten($argo-status-warning-color, 25); +$warning-foreground: $argo-status-warning-color; +$error-background: lighten($argo-failed-color, 20); +$error-foreground: $argo-failed-color-dark; +$in-progress-background: lighten($argo-running-color-dark, 40); +$in-progress-foreground: $argo-running-color-dark; +$inactive-background: lighten($argo-waiting-color, 20); +$inactive-foreground: $argo-waiting-color-dark; + +$space-unit: 4px; +$space-small: 8px; +$space-medium: 16px; +$space-large: 24px; + +$shadow-1: 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05); + +$font-family-primary: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol', 'Noto Color Emoji'; +$font-family-mono: sfmono-regular, Consolas, liberation mono, Menlo, Courier, monospace; diff --git a/ui/src/app/components/analysis-modal/transforms.test.ts b/ui/src/app/components/analysis-modal/transforms.test.ts new file mode 100644 index 0000000000..5c2c2a667e --- /dev/null +++ b/ui/src/app/components/analysis-modal/transforms.test.ts @@ -0,0 +1,548 @@ +// eslint-disable-file @typescript-eslint/ban-ts-comment + +import { + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Argument, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CloudWatchMetric, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricResult, +} from '../../../models/rollout/generated'; +import { + analysisEndTime, + analysisStartTime, + argValue, + chartMax, + conditionDetails, + formatKeyValueMeasurement, + formatMultiItemArrayMeasurement, + formatSingleItemArrayMeasurement, + formatThresholdsForChart, + formattedValue, + interpolateQuery, + isChartable, + isValidDate, + metricProvider, + metricStatusLabel, + metricSubstatus, + printableCloudWatchQuery, + printableDatadogQuery, +} from './transforms'; +import {AnalysisStatus, FunctionalStatus} from './types'; + +const MOCK_METRICS_WITHOUT_END_TIMES: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricResult[] = [ + { + measurements: [], + }, + { + measurements: [{}, {}], + }, +]; + +const MOCK_METRICS_WITH_END_TIMES: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricResult[] = [ + { + measurements: [ + { + // @ts-ignore + finishedAt: '2023-11-16T00:25:23Z', + }, + { + // @ts-ignore + finishedAt: '2023-11-16T00:26:23Z', + }, + { + // @ts-ignore + finishedAt: '2023-11-16T00:27:23Z', + }, + { + // @ts-ignore + finishedAt: '2023-11-16T00:28:23Z', + }, + ], + }, +]; + +const MOCK_ARGS: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Argument[] = [ + { + name: 'service-name', + value: 'istio-host-split-canary', + }, + { + name: 'application-name', + value: 'istio-host-split-canary', + }, + { + name: 'cpu-usage-threshold', + }, + { + name: 'success-rate-threshold', + value: '0.95', + }, + { + name: 'latency-threshold', + value: '500', + }, +]; + +const MOCK_PROVIDER_PROMETHEUS: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider = { + prometheus: { + address: 'https://prometheus-k8s.monitoring:9090', + query: 'sum(irate(istio_requests_total{destination_service_name=~"{{args.service-name}}",response_code!~"5.*"}[1m])) \n/\nsum(irate(istio_requests_total{destination_service_name=~"{{args.service-name}}"}[1m]))', + }, +}; +const MOCK_PROVIDER_NEWRELIC: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider = { + newRelic: { + query: "FROM Transaction SELECT percentage(count(*), WHERE httpResponseCode != 500) as successRate where appName = '{{ args.application-name }}'", + }, +}; +const MOCK_PROVIDER_DATADOG_V2_1: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider = { + datadog: { + apiVersion: 'v2', + query: 'sum:requests.errors{service:{{args.service-name}}}.as_count()', + formula: "moving_rollup(a, 60, 'sum') / b", + }, +}; +const MOCK_PROVIDER_DATADOG_V2_2: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider = { + datadog: { + apiVersion: 'v2', + queries: { + a: 'sum:requests.errors{service:{{args.service-name}}}.as_count()', + b: 'sum:requests{service:{{args.service-name}}}.as_count()', + }, + formula: "moving_rollup(a, 60, 'sum') / b", + }, +}; + +const MOCK_PROVIDER_CLOUDWATCH: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CloudWatchMetric = { + metricDataQueries: [ + { + id: 'rate', + expression: 'errors / requests', + }, + { + id: 'errors', + metricStat: { + metric: { + namespace: 'app', + metricName: 'errors', + }, + stat: 'Sum', + unit: 'Count', + }, + returnData: false, + }, + { + id: 'requests', + metricStat: { + metric: { + namespace: 'app', + metricName: 'requests', + }, + stat: 'Sum', + unit: 'Count', + }, + returnData: false, + }, + ], +}; + +const MOCK_ARGS_PROMETHEUS = [ + { + name: 'service-name', + value: 'istio-host-split-canary', + }, +]; +const MOCK_QUERY_PROMETHEUS = + 'sum(irate(istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="source",destination_service=~"{{args.service-name}}"}[5m]))'; + +const MOCK_ARGS_NEWRELIC = [{name: 'application-name', value: 'myApp'}]; +const MOCK_QUERY_NEWRELIC = "FROM Transaction SELECT percentage(count(*), WHERE httpResponseCode != 500) as successRate where appName = '{{ args.application-name }}'"; + +const MOCK_ARGS_DATADOG = [ + { + name: 'service-name', + value: 'istio-host-split-canary', + }, +]; +const MOCK_QUERY_DATADOG = 'sum:requests.error.rate{service:{{args.service-name}}}'; + +const MOCK_ARGS_WAVEFRONT = [ + { + name: 'service-name', + value: 'istio-host-split-canary', + }, +]; +const MOCK_QUERY_WAVEFRONT = + 'sum(rate(5m, ts("istio.requestcount.count", response_code!=500 and destination_service="{{args.service-name}}"))) / sum(rate(5m, ts("istio.requestcount.count", reporter=client and destination_service="{{args.service-name}}")))'; + +const MOCK_QUERY_GRAPHITE = + "target=summarize(asPercent(sumSeries(stats.timers.httpServerRequests.app.{{args.service-name}}.exception.*.method.*.outcome.{CLIENT_ERROR,INFORMATIONAL,REDIRECTION,SUCCESS}.status.*.uri.*.count), sumSeries(stats.timers.httpServerRequests.app.{{args.service-name}}.exception.*.method.*.outcome.*.status.*.uri.*.count)),'5min','avg')"; +const MOCK_ARGS_GRAPHITE = [ + { + name: 'service-name', + value: 'istio-host-split-canary', + }, +]; + +const MOCK_QUERY_INFLUXDB = + 'from(bucket: "app_istio") range(start: -15m) filter(fn: (r) => r["destination_workload"] == "{{ args.application-name }}")|> filter(fn: (r) => r["_measurement"] == "istio:istio_requests_errors_percentage:rate1m:5xx")'; +const MOCK_ARGS_INFLUXDB = [{name: 'application-name', value: 'myApp'}]; + +const MOCK_QUERY_SKYWALKING = + 'query queryData($duration: Duration!) { service_apdex: readMetricsValues(condition: { name: "service_apdex", entity: { scope: Service, serviceName: "{{ args.service-name }}", normal: true } }, duration: $duration) { label values { values { value } } } }'; +const MOCK_ARGS_SKYWALKING = [ + { + name: 'service-name', + value: 'istio-host-split-canary', + }, +]; + +const MOCK_CONDITION_1 = 'result[0] < .95'; +const MOCK_CONDITION_2 = 'result[0] > .5 && result[0] < .95'; +const MOCK_CONDITION_3 = 'result.successRate >= 0.95'; +const MOCK_CONDITION_4 = 'result.successRate >= {{ args.success-rate-threshold }}'; +const MOCK_CONDITION_5 = 'result.successRate >= {{ args.success-rate-threshold }} && result.errorRate <= 0.1'; + +describe('analysis modal transforms', () => { + beforeAll(() => {}); + afterAll(() => {}); + + test('isValidDate() for undefined', () => { + expect(isValidDate()).toBe(false); + }); + test('isValidDate() for a non-date recognized string', () => { + expect(isValidDate('abcd')).toBe(false); + }); + test('isValidDate() for a date recognized string', () => { + expect(isValidDate('2023-11-16T00:25:23Z')).toBe(true); + }); + + test('analysisStartTime() for undefined', () => { + expect(analysisStartTime()).toBeNull(); + }); + test('analysisStartTime() for a non-date recognized string', () => { + expect(analysisStartTime('abcd')).toBeNull(); + }); + test('analysisStartTime() for a date recognized string', () => { + expect(analysisStartTime('2023-11-16T00:25:23Z')).toBe(1700094323000); + }); + + test('analysisEndTime() for no metric results', () => { + expect(analysisEndTime([])).toBe(null); + }); + test('analysisEndTime() for analysis with metrics but no measurements', () => { + expect(analysisEndTime(MOCK_METRICS_WITHOUT_END_TIMES)).toBe(null); + }); + test('analysisEndTime() for measurements with finishedAt times', () => { + expect(analysisEndTime(MOCK_METRICS_WITH_END_TIMES)).toBe(1700094503000); + }); + + test('argValue() for empty args', () => { + expect(argValue([], 'cpu-threhold')).toBeNull(); + }); + test('argValue() for missing arg name / value', () => { + expect(argValue(MOCK_ARGS, 'memory-threshold')).toBeNull(); + }); + test('argValue() for missing arg value', () => { + expect(argValue(MOCK_ARGS, 'cpu-usage-treshold')).toBeNull(); + }); + test('argValue() for present arg name / value', () => { + expect(argValue(MOCK_ARGS, 'latency-threshold')).toBe('500'); + }); + + test('metricProvider() for known provider', () => { + expect(metricProvider(MOCK_PROVIDER_PROMETHEUS)).toBe('prometheus'); + }); + + test('conditionDetails with missing condition', () => { + expect(conditionDetails(undefined, MOCK_ARGS, MOCK_PROVIDER_PROMETHEUS)).toEqual({ + label: null, + thresholds: [], + conditionKeys: [], + }); + }); + test('conditionDetails() with missing provider', () => { + expect(conditionDetails(MOCK_CONDITION_1, MOCK_ARGS)).toEqual({ + label: null, + thresholds: [], + conditionKeys: [], + }); + }); + test('conditionDetails() for unsupported format', () => { + expect(conditionDetails('result in resultsArray', MOCK_ARGS, MOCK_PROVIDER_PROMETHEUS)).toEqual({ + label: 'result in resultsArray', + thresholds: [], + conditionKeys: [], + }); + }); + test('conditionDetails() with missing args', () => { + expect(conditionDetails(MOCK_CONDITION_1, undefined, MOCK_PROVIDER_PROMETHEUS)).toEqual({ + conditionKeys: ['0'], + label: 'result[0] < .95', + thresholds: [0.95], + }); + }); + test('conditionDetails() for condition like result[0] [>, <] [number]', () => { + expect(conditionDetails(MOCK_CONDITION_1, MOCK_ARGS, MOCK_PROVIDER_PROMETHEUS)).toEqual({ + conditionKeys: ['0'], + label: 'result[0] < .95', + thresholds: [0.95], + }); + }); + test('conditionDetails() for multiple conditions like result[0] [>, <] [number] && result[0] [>, <] [number]', () => { + expect(conditionDetails(MOCK_CONDITION_2, MOCK_ARGS, MOCK_PROVIDER_PROMETHEUS)).toEqual({ + conditionKeys: ['0'], + label: 'result[0] > .5 && result[0] < .95', + thresholds: [0.5, 0.95], + }); + }); + test('conditionDetails() for condition like result.[key] [>, <] [number]', () => { + expect(conditionDetails(MOCK_CONDITION_3, MOCK_ARGS, MOCK_PROVIDER_NEWRELIC)).toEqual({ + conditionKeys: ['successRate'], + label: 'result.successRate >= 0.95', + thresholds: [0.95], + }); + }); + test('conditionDetails() for condition like result.[key] [>, <] [arg value]', () => { + expect(conditionDetails(MOCK_CONDITION_4, MOCK_ARGS, MOCK_PROVIDER_NEWRELIC)).toEqual({ + conditionKeys: ['successRate'], + label: 'result.successRate >= 0.95', + thresholds: [0.95], + }); + }); + test('conditionDetails() for multiple condition like result.[key1] [>, <] [arg value] && result.[key2] [>, <] [number]', () => { + expect(conditionDetails(MOCK_CONDITION_5, MOCK_ARGS, MOCK_PROVIDER_NEWRELIC)).toEqual({ + conditionKeys: ['successRate', 'errorRate'], + label: 'result.successRate >= 0.95 && result.errorRate <= 0.1', + thresholds: [0.95, 0.1], + }); + }); + + test('formatThresholdsForChart() with number values', () => { + expect(formatThresholdsForChart([0, 1.1, 2.22, 3.333, 4.4444, 5.55555])).toEqual([0, 1.1, 2.22, 3.33, 4.44, 5.56]); + }); + + test('chartMax() for 0 max value and null thresholds', () => { + expect(chartMax(0, null, null)).toBe(1); + }); + test('chartMax() for 1 max value and null thresholds', () => { + expect(chartMax(1, null, null)).toBe(1.2); + }); + test('chartMax() for max value and thresholds that are the same', () => { + expect(chartMax(2, [2], [2])).toBe(2.4); + }); + test('chartMax() for max value that is above thresholds', () => { + expect(chartMax(4, [2, 3], [1, 2])).toBe(4.8); + }); + test('chartMax() for fail threshold that is above value and success threshold', () => { + expect(chartMax(2, [2, 3, 4], [1, 2])).toBe(4.8); + }); + test('chartMax() for success threshold that is above value and fail threshold', () => { + expect(chartMax(2, [2, 3, 4], [1, 2, 6])).toBe(7.2); + }); + + test('metricSubstatus() for metric with pending status', () => { + expect(metricSubstatus(AnalysisStatus.Pending, 0, 0, 0)).toBe(undefined); + }); + test('metricSubstatus() for successful metric with no issues', () => { + expect(metricSubstatus(AnalysisStatus.Successful, 0, 0, 0)).toBe(undefined); + }); + test('metricSubstatus() for successful metric with failures', () => { + expect(metricSubstatus(AnalysisStatus.Successful, 2, 0, 0)).toBe(FunctionalStatus.ERROR); + }); + test('metricSubstatus() for successful metric with errors', () => { + expect(metricSubstatus(AnalysisStatus.Successful, 0, 2, 0)).toBe(FunctionalStatus.WARNING); + }); + + test('metricStatusLabel() for metric with unknown status', () => { + expect(metricStatusLabel(AnalysisStatus.Unknown, 0, 0, 0)).toBe('Analysis status unknown'); + }); + test('metricStatusLabel() for metric with successful status with failures', () => { + expect(metricStatusLabel(AnalysisStatus.Successful, 1, 0, 0)).toBe('Analysis passed with measurement failures'); + }); + test('metricStatusLabel() for metric with successful status with errors', () => { + expect(metricStatusLabel(AnalysisStatus.Successful, 0, 1, 0)).toBe('Analysis passed with measurement errors'); + }); + test('metricStatusLabel() for metric with successful status with inconclusives', () => { + expect(metricStatusLabel(AnalysisStatus.Successful, 0, 0, 1)).toBe('Analysis passed with inconclusive measurements'); + }); + test('metricStatusLabel() for metric with successful status with multiple issues', () => { + expect(metricStatusLabel(AnalysisStatus.Successful, 1, 2, 3)).toBe('Analysis passed with multiple issues'); + }); + + test('interpolateQuery() for no query', () => { + expect(interpolateQuery(undefined, MOCK_ARGS)).toBe(undefined); + }); + test('interpolateQuery() for prometheus query with no args', () => { + expect(interpolateQuery(MOCK_QUERY_PROMETHEUS, [])).toBe(MOCK_QUERY_PROMETHEUS); + }); + test('interpolateQuery() for prometheus query and args', () => { + expect(interpolateQuery(MOCK_QUERY_PROMETHEUS, MOCK_ARGS_PROMETHEUS)).toBe( + 'sum(irate(istio_requests_total{reporter="source",destination_service=~"istio-host-split-canary",response_code!~"5.*"}[5m])) / sum(irate(istio_requests_total{reporter="source",destination_service=~"istio-host-split-canary"}[5m]))' + ); + }); + test('interpolateQuery() for newrelic query and args', () => { + expect(interpolateQuery(MOCK_QUERY_NEWRELIC, MOCK_ARGS_NEWRELIC)).toBe( + "FROM Transaction SELECT percentage(count(*), WHERE httpResponseCode != 500) as successRate where appName = 'myApp'" + ); + }); + test('interpolateQuery() for simple datadog query and args', () => { + expect(interpolateQuery(MOCK_QUERY_DATADOG, MOCK_ARGS_DATADOG)).toBe('sum:requests.error.rate{service:istio-host-split-canary}'); + }); + test('interpolateQuery() for wavefront query and args', () => { + expect(interpolateQuery(MOCK_QUERY_WAVEFRONT, MOCK_ARGS_WAVEFRONT)).toBe( + 'sum(rate(5m, ts("istio.requestcount.count", response_code!=500 and destination_service="istio-host-split-canary"))) / sum(rate(5m, ts("istio.requestcount.count", reporter=client and destination_service="istio-host-split-canary")))' + ); + }); + test('interpolateQuery() for graphite query and args', () => { + expect(interpolateQuery(MOCK_QUERY_GRAPHITE, MOCK_ARGS_GRAPHITE)).toBe( + "target=summarize(asPercent(sumSeries(stats.timers.httpServerRequests.app.istio-host-split-canary.exception.*.method.*.outcome.{CLIENT_ERROR,INFORMATIONAL,REDIRECTION,SUCCESS}.status.*.uri.*.count), sumSeries(stats.timers.httpServerRequests.app.istio-host-split-canary.exception.*.method.*.outcome.*.status.*.uri.*.count)),'5min','avg')" + ); + }); + test('interpolateQuery() for influxdb query and args', () => { + expect(interpolateQuery(MOCK_QUERY_INFLUXDB, MOCK_ARGS_INFLUXDB)).toBe( + 'from(bucket: "app_istio") range(start: -15m) filter(fn: (r) => r["destination_workload"] == "myApp")|> filter(fn: (r) => r["_measurement"] == "istio:istio_requests_errors_percentage:rate1m:5xx")' + ); + }); + test('interpolateQuery() for skywalking query and args', () => { + expect(interpolateQuery(MOCK_QUERY_SKYWALKING, MOCK_ARGS_SKYWALKING)).toBe( + 'query queryData($duration: Duration!) { service_apdex: readMetricsValues(condition: { name: "service_apdex", entity: { scope: Service, serviceName: "istio-host-split-canary", normal: true } }, duration: $duration) { label values { values { value } } } }' + ); + }); + + test('printableDataDogQuery() with v2 query and formula', () => { + expect(printableDatadogQuery(MOCK_PROVIDER_DATADOG_V2_1.datadog, MOCK_ARGS_DATADOG)).toStrictEqual([ + `query: sum:requests.errors{service:istio-host-split-canary}.as_count(), formula: moving_rollup(a, 60, 'sum') / b`, + ]); + }); + test('printableDataDogQuery() with v2 queries and formula', () => { + expect(printableDatadogQuery(MOCK_PROVIDER_DATADOG_V2_2.datadog, MOCK_ARGS_DATADOG)).toStrictEqual([ + `queries: {"a":"sum:requests.errors{service:istio-host-split-canary}.as_count()","b":"sum:requests{service:istio-host-split-canary}.as_count()"}, formula: moving_rollup(a, 60, 'sum') / b`, + ]); + }); + + test('printableCloudWatchQuery() with metricDataQueries', () => { + expect(printableCloudWatchQuery(MOCK_PROVIDER_CLOUDWATCH)).toStrictEqual([ + '{"id":"rate","expression":"errors / requests"}', + '{"id":"errors","metricStat":{"metric":{"namespace":"app","metricName":"errors"},"stat":"Sum","unit":"Count"},"returnData":false}', + '{"id":"requests","metricStat":{"metric":{"namespace":"app","metricName":"requests"},"stat":"Sum","unit":"Count"},"returnData":false}', + ]); + }); + + test('isChartable() for undefined', () => { + expect(isChartable(undefined)).toBe(false); + }); + test('isChartable() for null', () => { + expect(isChartable(null)).toBe(true); + }); + test('isChartable() for a string', () => { + expect(isChartable('abc')).toBe(false); + }); + test('isChartable() for an array', () => { + expect(isChartable([1, 2, 5, 3])).toBe(false); + }); + test('isChartable() for a positive number', () => { + expect(isChartable(5)).toBe(true); + }); + test('isChartable() for a negative number', () => { + expect(isChartable(-5)).toBe(true); + }); + + test('formattedValue() for null', () => { + expect(formattedValue(null)).toBe(null); + }); + test('formattedValue() for an int', () => { + expect(formattedValue(1)).toBe(1); + }); + test('formattedValue() for a float', () => { + expect(formattedValue(1.2653)).toBe(1.27); + }); + test('formattedValue() for a string', () => { + expect(formattedValue('abc')).toBe('abc'); + }); + test('formattedValue() for an array of numbers', () => { + expect(formattedValue([1, 4, 3, 7])).toBe('1,4,3,7'); + }); + + test('formatSingleItemArrayMeasurement() with out of bounds accessor', () => { + expect(formatSingleItemArrayMeasurement([4], 1)).toEqual({ + canChart: true, + chartValue: {1: null}, + tableValue: {1: null}, + }); + }); + test('formatSingleItemArrayMeasurement() for a value like [`abc`] with accessor 0', () => { + expect(formatSingleItemArrayMeasurement(['abc'], 0)).toEqual({ + canChart: false, + tableValue: {0: 'abc'}, + }); + }); + test('formatSingleItemArrayMeasurement() for a value like [4] with accessor 0', () => { + expect(formatSingleItemArrayMeasurement([4], 0)).toEqual({ + canChart: true, + chartValue: {0: 4}, + tableValue: {0: 4}, + }); + }); + test('formatSingleItemArrayMeasurement() for a value like [null] with accessor 0', () => { + expect(formatSingleItemArrayMeasurement([null], 0)).toEqual({ + canChart: true, + chartValue: {0: null}, + tableValue: {0: null}, + }); + }); + + test('formatMultiItemArrayMeasurement() with an empty array', () => { + expect(formatMultiItemArrayMeasurement([])).toEqual({ + canChart: false, + tableValue: '', + }); + }); + test('formatMultiItemArrayMeasurement() with all numbers', () => { + expect(formatMultiItemArrayMeasurement([4, 6, 3, 5])).toEqual({ + canChart: true, + chartValue: 4, + tableValue: '4,6,3,5', + }); + }); + test('formatMultiItemArrayMeasurement() with null as the first item', () => { + expect(formatMultiItemArrayMeasurement([null, 6, 3, 5])).toEqual({ + canChart: true, + chartValue: null, + tableValue: 'null,6,3,5', + }); + }); + test('formatMultiItemArrayMeasurement() with a string as the first item', () => { + expect(formatMultiItemArrayMeasurement(['abc', 6, 3, 5])).toEqual({ + canChart: false, + tableValue: 'abc,6,3,5', + }); + }); + + test('formatKeyValueMeasurement() with key value pairs and no matching accessors', () => { + expect(formatKeyValueMeasurement({cpuUsage: 50, latency: 500}, ['errorRate'])).toEqual({ + canChart: false, + chartValue: {errorRate: null}, + tableValue: {errorRate: null}, + }); + }); + test('formatKeyValueMeasurement() with key value pairs and a single matching accessor', () => { + expect(formatKeyValueMeasurement({cpuUsage: 50, latency: 500}, ['latency'])).toEqual({ + canChart: true, + chartValue: {latency: 500}, + tableValue: {latency: 500}, + }); + }); + test('formatKeyValueMeasurement() with key value pairs and multiple matching accessors', () => { + expect(formatKeyValueMeasurement({cpuUsage: 50, latency: 500}, ['latency', 'cpuUsage'])).toEqual({ + canChart: true, + chartValue: {latency: 500, cpuUsage: 50}, + tableValue: {latency: 500, cpuUsage: 50}, + }); + }); + test('formatKeyValueMeasurement() with key value pairs all null and matching accessors', () => { + expect(formatKeyValueMeasurement({cpuUsage: null, latency: null}, ['latency', 'cpuUsage'])).toEqual({ + canChart: false, + chartValue: {latency: null, cpuUsage: null}, + tableValue: {latency: null, cpuUsage: null}, + }); + }); +}); diff --git a/ui/src/app/components/analysis-modal/transforms.ts b/ui/src/app/components/analysis-modal/transforms.ts new file mode 100644 index 0000000000..a655dda1c4 --- /dev/null +++ b/ui/src/app/components/analysis-modal/transforms.ts @@ -0,0 +1,648 @@ +// eslint-disable-file @typescript-eslint/ban-ts-comment +import * as moment from 'moment'; + +import { + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Argument, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CloudWatchMetric, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1DatadogMetric, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Measurement, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricResult, + RolloutAnalysisRunSpecAndStatus, +} from '../../../models/rollout/generated'; +import {AnalysisStatus, FunctionalStatus, MeasurementSetInfo, MeasurementValueInfo, TransformedMeasurement, TransformedMetric, TransformedValueObject} from './types'; + +export const isFiniteNumber = (value: any) => Number.isFinite(value); + +export const roundNumber = (value: number): number => Math.round(value * 100) / 100; + +export const isValidDate = (value?: string): boolean => value !== undefined && moment(value).isValid(); + +// Overall Analysis Utils + +/** + * + * @param startTime start time of the analysis run + * @returns timestamp in ms or null + */ +export const analysisStartTime = (startTime?: string): number | null => (isValidDate(startTime) ? new Date(startTime).getTime() : null); + +/** + * + * @param metricResults array of metric results + * @returns timestamp in ms or null + */ +export const analysisEndTime = (metricResults: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricResult[]): number | null => { + if (metricResults.length === 0) { + return null; + } + + const measurementEndTimes: number[] = []; + metricResults.forEach((metricResult) => { + (metricResult.measurements ?? []).forEach((measurement) => { + // @ts-ignore + if (isValidDate(measurement.finishedAt)) { + // @ts-ignore + measurementEndTimes.push(new Date(measurement.finishedAt).getTime()); + } + }); + }); + + const latestTime = Math.max(...measurementEndTimes); + return isFiniteNumber(latestTime) ? latestTime : null; +}; + +// Arg Utils + +/** + * + * @param args arguments name/value pairs associated with the analysis run + * @param argName name of arg for which to find the value + * @returns + * value associated with the arg + * or null if args is empty + * or null if argName is not present in args + * or null if arg value is undefined or null + */ +export const argValue = (args: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Argument[], argName: string): string | null => + args.find((arg) => arg.name === argName)?.value ?? null; + +// Metric Utils + +/** + * + * @param providerInfo metric provider object + * @returns first key in the provider object + */ +export const metricProvider = (providerInfo: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider): string => + Object.keys(providerInfo)?.[0] ?? 'unsupported provider'; + +const PROVIDER_CONDITION_SUPPORT: { + [key: string]: (resultAccessor: string) => { + isFormatSupported: boolean; + conditionKey: string | null; + }; +} = { + prometheus: (resultAccessor: string) => ({ + isFormatSupported: resultAccessor === 'result[0]', + conditionKey: '0', + }), + datadog: (resultAccessor: string) => ({ + isFormatSupported: ['result', 'default(result, 0)'].includes(resultAccessor), + conditionKey: resultAccessor.includes('0') ? '0' : null, + }), + wavefront: (resultAccessor: string) => ({ + isFormatSupported: resultAccessor === 'result', + conditionKey: null, + }), + newRelic: (resultAccessor: string) => ({ + isFormatSupported: resultAccessor.startsWith('result.'), + conditionKey: resultAccessor.substring(7), + }), + cloudWatch: (resultAccessor: string) => ({ + isFormatSupported: false, + conditionKey: null, + }), + graphite: (resultAccessor: string) => ({ + isFormatSupported: resultAccessor === 'result[0]', + conditionKey: '0', + }), + influxdb: (resultAccessor: string) => ({ + isFormatSupported: resultAccessor === 'result[0]', + conditionKey: '0', + }), + skywalking: (resultAccessor: string) => ({ + isFormatSupported: false, + conditionKey: null, + }), +}; + +/** + * + * @param condition failure_condition or success_condition with the format + * [result accessor] [operator] {{ args.[argname] }} + * or [result accessor] [operator] [value] + * @param args arguments name/value pairs associated with the analysis run + * @returns + * label - a friendly fail/success condition label and + * thresholds - threshold values that can be converted into numbers + * conditionKeys - string keys for the values being compared in the condition + */ +export const conditionDetails = ( + condition?: string, + args: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Argument[] = [], + provider?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider +): { + label: string | null; + thresholds: number[]; + conditionKeys: string[]; +} => { + if (condition === undefined || condition === '' || provider === undefined || metricProvider(provider) === 'unsupported provider') { + return { + label: null, + thresholds: [], + conditionKeys: [], + }; + } + + const interpolatedCondition = interpolateQuery(condition, args); + const subconditions = interpolatedCondition.split(/ && | \|\| /); + + const providerType = metricProvider(provider); + const thresholds: number[] = []; + const conditionKeys: string[] = []; + + // for each subcondition, if it deemed to be a supported subcondition, add keys and numeric thresholds + subconditions.forEach((subcondition) => { + const subconditionParts = subcondition.split(' '); + if (subconditionParts.length === 3) { + const providerInfo = PROVIDER_CONDITION_SUPPORT[providerType]?.(subconditionParts[0].trim()); + const isFormatSupported = providerInfo?.isFormatSupported ?? false; + const conditionKey = providerInfo?.conditionKey ?? null; + + const isUnderOverThreshold = subconditionParts[1].includes('<') || subconditionParts[1].includes('>'); + const isChartableThreshold = isFiniteNumber(parseFloat(subconditionParts[2])); + + if (isFormatSupported && isUnderOverThreshold && isChartableThreshold) { + if (conditionKey !== null) { + conditionKeys.push(conditionKey); + } + thresholds.push(Number(subconditionParts[2])); + } + } + }); + + return { + label: interpolatedCondition, + thresholds, + conditionKeys: [...new Set(conditionKeys)], + }; +}; + +/** + * + * @param thresholds threshold values + * @returns number formatted to two decimal points + */ +export const formatThresholdsForChart = (thresholds: number[]): (number | null)[] => thresholds.map((t) => roundNumber(t)); + +/** + * + * @param valueMax max value for a measurement + * @param failThresholds fail thresholds for the metric + * @param successThresholds success thresholds for the metric + * @returns 120% of the max content value which could either be a data point or one of the thresholds + * or 1 if the max value is less than 1 and there are no thresholds + */ +export const chartMax = (valueMax: number, failThresholds: number[] | null, successThresholds: number[] | null) => { + if (valueMax < 1 && failThresholds === null && successThresholds === null) { + return 1; + } + const failThresholdMax = failThresholds !== null && failThresholds.length > 0 ? Math.max(...failThresholds) : Number.NEGATIVE_INFINITY; + const successThresholdMax = successThresholds !== null && successThresholds.length > 0 ? Math.max(...successThresholds) : Number.NEGATIVE_INFINITY; + return roundNumber(Math.max(valueMax, failThresholdMax, successThresholdMax) * 1.2); +}; + +/** + * + * @param phase analysis phase + * @returns analysis phase adjusted to render the UI status with a more accurate functional status + */ +export const getAdjustedMetricPhase = (phase?: AnalysisStatus): AnalysisStatus => (phase === AnalysisStatus.Error ? AnalysisStatus.Failed : phase ?? AnalysisStatus.Unknown); + +/** + * + * @param specAndStatus analysis spec and status information + * @returns analysis metrics with additional information to render to the UI + */ +export const transformMetrics = (specAndStatus?: RolloutAnalysisRunSpecAndStatus): {[key: string]: TransformedMetric} => { + if (specAndStatus?.spec === undefined || specAndStatus?.status === undefined) { + return {}; + } + + const {spec, status} = specAndStatus; + + const transformedMetrics: {[key: string]: TransformedMetric} = {}; + status.metricResults?.forEach((metricResults, idx) => { + const metricName = metricResults?.name ?? `Unknown metric ${idx}`; + const metricSpec = spec?.metrics?.find((m) => m.name === metricName); + + if (metricSpec !== undefined) { + // spec values + const failConditionInfo = conditionDetails(metricSpec.failureCondition, spec.args, metricSpec.provider); + const failThresholds = failConditionInfo.thresholds.length > 0 ? formatThresholdsForChart(failConditionInfo.thresholds) : null; + const successConditionInfo = conditionDetails(metricSpec.successCondition, spec.args, metricSpec.provider); + const successThresholds = successConditionInfo.thresholds.length > 0 ? formatThresholdsForChart(successConditionInfo.thresholds) : null; + + // value keys are needed for measurement values formatted as {key1: value1, key2: value2} + const conditionKeys = [...new Set([...failConditionInfo.conditionKeys, ...successConditionInfo.conditionKeys])]; + + // results values + const transformedMeasurementInfo = transformMeasurements(conditionKeys, metricResults?.measurements); + const {measurements, chartable, min, max} = transformedMeasurementInfo; + + const metricStatus = (metricResults?.phase ?? AnalysisStatus.Unknown) as AnalysisStatus; + const measurementFailures = metricResults?.failed ?? 0; + const measurementErrors = metricResults?.error ?? 0; + const measurementInconclusives = metricResults?.inconclusive ?? 0; + + transformedMetrics[metricName] = { + name: metricName, + spec: { + ...metricSpec, + queries: metricQueries(metricSpec.provider, spec.args), + failConditionLabel: failConditionInfo.label, + failThresholds, + successConditionLabel: successConditionInfo.label, + successThresholds, + conditionKeys, + }, + status: { + ...metricResults, + adjustedPhase: getAdjustedMetricPhase(metricStatus), + statusLabel: metricStatusLabel(metricStatus, measurementFailures, measurementErrors, measurementInconclusives), + substatus: metricSubstatus(metricStatus, measurementFailures, measurementErrors, measurementInconclusives), + transformedMeasurements: measurements, + chartable, + chartMin: min, + chartMax: chartMax(max, failThresholds, successThresholds), + }, + }; + } + }); + + return transformedMetrics; +}; + +/** + * + * @param status analysis metric status + * @param failures number of measurement failures + * @param errors number of measurement errors + * @param inconclusives number of inconclusive measurements + * @returns ui state substatus to indicate that there were errors/failures/ + * inconclusives + */ +export const metricSubstatus = (status: AnalysisStatus, failures: number, errors: number, inconclusives: number): FunctionalStatus.ERROR | FunctionalStatus.WARNING | undefined => { + switch (status) { + case AnalysisStatus.Pending: + case AnalysisStatus.Failed: + case AnalysisStatus.Inconclusive: + case AnalysisStatus.Error: + return undefined; + case AnalysisStatus.Running: + case AnalysisStatus.Successful: + if (failures > 0) { + return FunctionalStatus.ERROR; + } + if (errors > 0 || inconclusives > 0) { + return FunctionalStatus.WARNING; + } + return undefined; + default: + return undefined; + } +}; + +/** + * + * @param status analysis metric status + * @param failures number of measurement failures + * @param errors number of measurement errors + * @param inconclusives number of inconclusive measurements + * @returns descriptive label to include more details beyond the overall + * analysis status + */ +export const metricStatusLabel = (status: AnalysisStatus, failures: number, errors: number, inconclusives: number) => { + let extraDetails = ''; + const hasFailures = failures > 0; + const hasErrors = errors > 0; + const hasInconclusives = inconclusives > 0; + switch (status) { + case AnalysisStatus.Unknown: + return 'Analysis status unknown'; + case AnalysisStatus.Pending: + return 'Analysis pending'; + case AnalysisStatus.Running: + return 'Analysis in progress'; + case AnalysisStatus.Failed: + return `Analysis failed`; + case AnalysisStatus.Inconclusive: + return `Analysis inconclusive`; + case AnalysisStatus.Error: + return 'Analysis errored'; + case AnalysisStatus.Successful: + if (hasFailures && !hasErrors && !hasInconclusives) { + extraDetails = 'with measurement failures'; + } else if (!hasFailures && hasErrors && !hasInconclusives) { + extraDetails = 'with measurement errors'; + } else if (!hasFailures && !hasErrors && hasInconclusives) { + extraDetails = 'with inconclusive measurements'; + } else if (hasFailures || hasErrors || hasInconclusives) { + extraDetails = 'with multiple issues'; + } + return `Analysis passed ${extraDetails}`.trim(); + default: + return ''; + } +}; + +/** + * + * @param query query for an analysis run metric + * @param args arguments name/value pairs associated with the analysis run + * @returns the query with all {{ args.[argName] }} replaced with + * the value of the arg + */ +export const interpolateQuery = (query?: string, args?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Argument[]) => { + if (query === undefined) { + return undefined; + } + if (args === undefined || args.length === 0) { + return query; + } + + const regex = /\{{.*?\}}/g; + return query.replace(regex, (match) => { + const argPieces = match.replace(/[{ }]/g, '').split('.'); + const replacementValue = argValue(args, argPieces?.[1] ?? ''); + return replacementValue ?? match; + }); +}; + +/** + * + * @param datadog datadog metric object + * @param args arguments name/value pairs associated with the analysis run + * @returns query formatted for display or undefined + */ +export const printableDatadogQuery = ( + datadog: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1DatadogMetric, + args: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Argument[] +): string[] | undefined => { + if ((datadog.apiVersion ?? '').toLowerCase() === 'v1' && 'query' in datadog) { + return [interpolateQuery(datadog.query, args)]; + } + if ((datadog.apiVersion ?? '').toLowerCase() === 'v2') { + if ('query' in datadog) { + return 'formula' in datadog ? [`query: ${interpolateQuery(datadog.query, args)}, formula: ${datadog.formula}`] : [interpolateQuery(datadog.query, args)]; + } + if ('queries' in datadog) { + let interpolatedQueries: {[key: string]: string} = {}; + Object.keys(datadog.queries).forEach((queryKey) => { + interpolatedQueries[queryKey] = interpolateQuery(datadog.queries[queryKey], args); + }); + return 'formula' in datadog + ? [`queries: ${JSON.stringify(interpolatedQueries)}, formula: ${datadog.formula}`] + : Object.values(datadog.queries).map((query) => interpolateQuery(query, args)); + } + } + return undefined; +}; + +/** + * + * @param cloudWatch cloudwatch metric object + * @returns query formatted for display or undefined + */ +export const printableCloudWatchQuery = (cloudWatch: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1CloudWatchMetric): string[] | undefined => { + return Array.isArray(cloudWatch.metricDataQueries) ? cloudWatch.metricDataQueries.map((query) => JSON.stringify(query)) : undefined; +}; + +/** + * + * @param provider metric provider object + * @param args arguments name/value pairs associated with the analysis run + * @returns query formatted for display or undefined + */ +export const metricQueries = ( + provider?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricProvider | null, + args: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Argument[] = [] +): string[] | undefined => { + if (provider === undefined || provider === null) { + return undefined; + } + const providerType = metricProvider(provider); + switch (providerType) { + case 'prometheus': + return [interpolateQuery(provider.prometheus.query, args)]; + case 'datadog': + return printableDatadogQuery(provider.datadog, args); + case 'wavefront': + return [interpolateQuery(provider.wavefront.query, args)]; + case 'newRelic': + return [interpolateQuery(provider.newRelic.query, args)]; + case 'cloudWatch': + return printableCloudWatchQuery(provider.cloudWatch); + case 'graphite': + return [interpolateQuery(provider.graphite.query, args)]; + case 'influxdb': + return [interpolateQuery(provider.influxdb.query, args)]; + case 'skywalking': + return [interpolateQuery(provider.skywalking.query, args)]; + // not currently supported: kayenta, web, job, plugin + default: + return undefined; + } +}; + +// Measurement Utils + +/** + * + * @param conditionKeys keys from success/fail conditions used in some cases to pull values from the measurement result + * @param measurements array of metric measurements + * @returns formatted measurement values and chart information if the metric can be charted + */ +export const transformMeasurements = (conditionKeys: string[], measurements?: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Measurement[]): MeasurementSetInfo => { + if (measurements === undefined || measurements.length === 0) { + return { + chartable: false, + min: 0, + max: null, + measurements: [], + }; + } + + return measurements.reduce( + ( + acc: {chartable: boolean; min: number; max: number | null; measurements: TransformedMeasurement[]}, + currMeasurement: GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Measurement + ) => { + const transformedValue = transformMeasurementValue(conditionKeys, currMeasurement.value); + const {canChart, tableValue} = transformedValue; + const canCompareToBoundaries = canChart && transformedValue.chartValue !== null && isFiniteNumber(transformedValue.chartValue ?? null); + + return { + chartable: acc.chartable && canChart, + min: canCompareToBoundaries ? Math.min(Number(transformedValue.chartValue), acc.min) : acc.min, + max: canCompareToBoundaries ? Math.max(Number(transformedValue.chartValue), acc.max ?? 0) : acc.max, + measurements: [ + ...acc.measurements, + { + ...currMeasurement, + chartValue: transformedValue.chartValue, + tableValue, + }, + ], + }; + }, + {chartable: true, min: 0, max: null, measurements: [] as TransformedMeasurement[]} + ); +}; + +/** + * + * @param value value to check for chartability + * @returns whether the data point can be added to a line chart (number or null) + */ +export const isChartable = (value: any): boolean => isFiniteNumber(value) || value === null; + +type FormattedMeasurementValue = number | string | null; + +/** + * + * @param value value to display + * @returns value formatted for display purposes + */ +export const formattedValue = (value: any): FormattedMeasurementValue => { + const isNum = isFiniteNumber(value); + return isNum ? roundNumber(Number(value)) : value?.toString() ?? null; +}; + +/** + * + * @param value measurement value number (examples: 4 or 4.05) + * @returns information about displaying the measurement value + */ +const formatNumberMeasurement = (value: number): MeasurementValueInfo => { + const displayValue = formattedValue(value); + return { + canChart: true, + chartValue: displayValue, + tableValue: displayValue, + }; +}; + +/** + * + * @param value measurement value array (examples: [4] or [null] or ['anything else']) + * @param accessor key by which to access measurement value + * @returns information about displaying the measurement value + */ +export const formatSingleItemArrayMeasurement = (value: FormattedMeasurementValue[], accessor: number): MeasurementValueInfo => { + if (isFiniteNumber(accessor)) { + const measurementValue = value?.[accessor] ?? null; + // if it's a number or null, chart it + if (isFiniteNumber(measurementValue) || measurementValue === null) { + const displayValue = formattedValue(measurementValue); + return { + canChart: isChartable(measurementValue), + chartValue: {[accessor]: displayValue}, + tableValue: {[accessor]: displayValue}, + }; + } + // if it exists, but it's not a good format, just put it in a table + return { + canChart: false, + tableValue: {[accessor]: measurementValue.toString()}, + }; + } + return { + canChart: false, + tableValue: value.toString(), + }; +}; + +/** + * + * @param value measurement value array (examples: [4,6,3,5] or [4,6,null,5] or [4,6,'a string',5]) + * @returns information about displaying the measurement value (charts a chartable first value, shows stringified value in table)) + */ +export const formatMultiItemArrayMeasurement = (value: FormattedMeasurementValue[]): MeasurementValueInfo => { + if (value.length === 0) { + return { + canChart: false, + tableValue: '', + }; + } + + const firstMeasurementValue = value[0]; + const canChartFirstValue = isChartable(firstMeasurementValue); + return { + canChart: canChartFirstValue, + ...(canChartFirstValue && {chartValue: formattedValue(firstMeasurementValue)}), + tableValue: value.map((v) => String(v)).toString(), + }; +}; + +/** + * + * @param value measurement value object (example: { key1: 5, key2: 154, key3: 'abc' } + * @param accessors keys by which to access measurement values + * @returns information about displaying the measurement value (returns TransformedObjectValue)) + */ +export const formatKeyValueMeasurement = (value: {[key: string]: FormattedMeasurementValue}, accessors: string[]): MeasurementValueInfo => { + const transformedValue: TransformedValueObject = {}; + let canChart = true; + accessors.forEach((accessor) => { + if (accessor in value) { + const measurementValue = value[accessor]; + const displayValue = formattedValue(measurementValue); + canChart = canChart && isChartable(measurementValue); + transformedValue[accessor] = displayValue; + } else { + transformedValue[accessor] = null; + } + }); + return { + canChart: canChart && !Object.values(transformedValue).every((v: FormattedMeasurementValue) => v === null), + chartValue: transformedValue, + tableValue: transformedValue, + }; +}; + +/** + * + * @param conditionKeys keys from success/fail conditions used in some cases to pull values from the measurement result + * @param value measurement value returned by provider + * @returns chart and table data along with a flag indicating whether the measurement value can be charted + */ +const transformMeasurementValue = (conditionKeys: string[], value?: string): MeasurementValueInfo => { + if (value === undefined || value === '') { + return { + canChart: true, + chartValue: null, + tableValue: null, + }; + } + + const parsedValue = JSON.parse(value); + + // single number measurement value + if (isFiniteNumber(parsedValue)) { + return formatNumberMeasurement(parsedValue); + } + + // single item array measurement value + if (Array.isArray(parsedValue) && parsedValue.length > 0 && conditionKeys.length === 1) { + const accessor = parseInt(conditionKeys[0]); + return formatSingleItemArrayMeasurement(parsedValue, accessor); + } + + // multi-item array measurement value + if (Array.isArray(parsedValue) && parsedValue.length > 0) { + return formatMultiItemArrayMeasurement(parsedValue); + } + + // key / value pairs measurement value + if (typeof parsedValue === 'object' && !Array.isArray(parsedValue) && conditionKeys.length > 0) { + return formatKeyValueMeasurement(parsedValue, conditionKeys); + } + + // unsupported formats are stringified and put into table + return { + canChart: false, + tableValue: parsedValue.toString(), + }; +}; diff --git a/ui/src/app/components/analysis-modal/types.ts b/ui/src/app/components/analysis-modal/types.ts new file mode 100644 index 0000000000..5b4479a16b --- /dev/null +++ b/ui/src/app/components/analysis-modal/types.ts @@ -0,0 +1,70 @@ +import { + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Measurement, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Metric, + GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricResult, +} from '../../../models/rollout/generated'; + +export enum AnalysisStatus { + Successful = 'Successful', + Error = 'Error', + Failed = 'Failed', + Running = 'Running', + Pending = 'Pending', + Inconclusive = 'Inconclusive', + Unknown = 'Unknown', // added by frontend +} + +export enum FunctionalStatus { + ERROR = 'ERROR', + INACTIVE = 'INACTIVE', + IN_PROGRESS = 'IN_PROGRESS', + SUCCESS = 'SUCCESS', + WARNING = 'WARNING', +} + +export type TransformedMetricStatus = GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1MetricResult & { + adjustedPhase: AnalysisStatus; + chartable: boolean; + chartMax: number | null; + chartMin: number; + statusLabel: string; + substatus?: FunctionalStatus.ERROR | FunctionalStatus.WARNING; + transformedMeasurements: TransformedMeasurement[]; +}; + +export type TransformedMetricSpec = GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Metric & { + failConditionLabel: string | null; + failThresholds: number[] | null; + queries?: string[]; + successConditionLabel: string | null; + successThresholds: number[] | null; + conditionKeys: string[]; +}; + +export type TransformedMetric = { + name: string; + spec?: TransformedMetricSpec; + status: TransformedMetricStatus; +}; + +export type TransformedValueObject = { + [key: string]: number | string | null; +}; + +export type TransformedMeasurement = GithubComArgoprojArgoRolloutsPkgApisRolloutsV1alpha1Measurement & { + chartValue?: TransformedValueObject | number | string | null; + tableValue: TransformedValueObject | number | string | null; +}; + +export type MeasurementSetInfo = { + chartable: boolean; + max: number | null; + measurements: TransformedMeasurement[]; + min: number; +}; + +export type MeasurementValueInfo = { + canChart: boolean; + chartValue?: TransformedValueObject | number | string | null; + tableValue: TransformedValueObject | number | string | null; +}; diff --git a/ui/src/app/components/pods/pods.tsx b/ui/src/app/components/pods/pods.tsx index 1107a8444d..ec0d65eead 100644 --- a/ui/src/app/components/pods/pods.tsx +++ b/ui/src/app/components/pods/pods.tsx @@ -1,14 +1,16 @@ import * as React from 'react'; import * as moment from 'moment'; -import {Duration, Ticker} from 'argo-ui'; +import {DropDown, Duration} from 'argo-ui'; import {RolloutReplicaSetInfo} from '../../../models/rollout/generated'; import {ReplicaSetStatus, ReplicaSetStatusIcon} from '../status-icon/status-icon'; import './pods.scss'; -import {Dropdown, MenuProps, Tooltip} from 'antd'; +import {Tooltip} from 'antd'; + import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {IconDefinition, faCheck, faCircleNotch, faClipboard, faExclamationTriangle, faQuestionCircle, faTimes} from '@fortawesome/free-solid-svg-icons'; import {EllipsisMiddle} from '../ellipsis-middle/ellipsis-middle'; import {InfoItem} from '../info-item/info-item'; +import {Ticker} from '../ticker/ticker'; export enum PodStatus { Pending = 'pending', @@ -48,15 +50,11 @@ export const ReplicaSets = (props: {replicaSets: RolloutReplicaSetInfo[]; showRe return (
    - {replicaSets?.map( - (rsInfo) => - rsInfo.pods && - rsInfo.pods.length > 0 && ( -
    - -
    - ), - )} + {replicaSets?.map((rsInfo) => ( +
    + +
    + ))}
    ); }; @@ -84,9 +82,8 @@ export const ReplicaSet = (props: {rs: RolloutReplicaSetInfo; showRevision?: boo Scaledown in - } - > - ) as any} icon='fa fa-clock'> + }> + ) as any} icon='fa fa-clock' /> ); }} @@ -97,40 +94,27 @@ export const ReplicaSet = (props: {rs: RolloutReplicaSetInfo; showRevision?: boo )} - {props.rs.pods && props.rs.pods.length > 0 && ( -
    - {(props.rs?.pods || []).map((pod, i) => ( - -
    Status: {pod.status}
    -
    {pod.objectMeta?.name}
    -
    - } - /> - ))} -
    - )} +
    + {(props.rs?.pods || []).length > 0 + ? (props.rs?.pods || []).map((pod, i) => ( + +
    Status: {pod.status}
    +
    {pod.objectMeta?.name}
    +
    + } + /> + )) + : 'No Pods!'} + ); }; -const CopyMenu = (name: string): MenuProps['items'] => { - return [ - { - key: 1, - label: ( -
    navigator.clipboard.writeText(name)}> - Copy Name -
    - ), - }, - ]; -}; - export const PodWidget = ({name, status, tooltip, customIcon}: {name: string; status: string; tooltip: React.ReactNode; customIcon?: IconDefinition}) => { let icon: IconDefinition; let spin = false; @@ -172,12 +156,18 @@ export const PodWidget = ({name, status, tooltip, customIcon}: {name: string; st } return ( - - -
    - -
    -
    -
    + ( + +
    + +
    +
    + )}> +
    navigator.clipboard.writeText(name)}> + Copy Name +
    +
    ); }; diff --git a/ui/src/app/components/rollout/revision.tsx b/ui/src/app/components/rollout/revision.tsx index bd7f7410ce..394a3d09fe 100644 --- a/ui/src/app/components/rollout/revision.tsx +++ b/ui/src/app/components/rollout/revision.tsx @@ -1,16 +1,22 @@ import * as React from 'react'; +import * as moment from 'moment'; import {RolloutAnalysisRunInfo, RolloutExperimentInfo, RolloutReplicaSetInfo} from '../../../models/rollout/generated'; import {IconForTag} from '../../shared/utils/utils'; -import {PodWidget, ReplicaSets} from '../pods/pods'; +import {ReplicaSets} from '../pods/pods'; import {ImageInfo, parseImages} from './rollout'; import './rollout.scss'; import '../pods/pods.scss'; import {ConfirmButton} from '../confirm-button/confirm-button'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import {faChartBar, faChevronCircleDown, faChevronCircleUp, faUndoAlt} from '@fortawesome/free-solid-svg-icons'; -import {Button, Tooltip} from 'antd'; -import moment = require('moment'); +import {faChevronCircleDown, faChevronCircleUp, faUndoAlt} from '@fortawesome/free-solid-svg-icons'; +import {Button, Space, Tooltip, Typography} from 'antd'; import {InfoItemProps, InfoItemRow} from '../info-item/info-item'; +import {AnalysisModal} from '../analysis-modal/analysis-modal'; +import StatusIndicator from '../analysis-modal/status-indicator/status-indicator'; +import {AnalysisStatus} from '../analysis-modal/types'; +import {getAdjustedMetricPhase} from '../analysis-modal/transforms'; + +const {Text} = Typography; function formatTimestamp(ts: string): string { const inputFormat = 'YYYY-MM-DD HH:mm:ss Z z'; @@ -49,27 +55,25 @@ interface RevisionWidgetProps { initCollapsed?: boolean; rollback?: (revision: number) => void; current: boolean; - message: String; } -export const RevisionWidget = (props: RevisionWidgetProps) => { - const {revision, initCollapsed} = props; +export const RevisionWidget = ({current, initCollapsed, revision, rollback}: RevisionWidgetProps) => { const [collapsed, setCollapsed] = React.useState(initCollapsed); const icon = collapsed ? faChevronCircleDown : faChevronCircleUp; - const images = parseImages(revision.replicaSets); + const images = parseImages(revision.replicaSets ?? []); const hasPods = (revision.replicaSets || []).some((rs) => rs.pods?.length > 0); + return (
    Revision {revision.number}
    - {!props.current && props.rollback && ( + {!current && rollback && ( props.rollback(Number(revision.number))} + onClick={() => rollback(Number(revision.number))} type='default' icon={} - style={{fontSize: '13px', marginRight: '10px'}} - > + style={{fontSize: '13px', marginRight: '10px'}}> Rollback )} @@ -84,11 +88,9 @@ export const RevisionWidget = (props: RevisionWidgetProps) => { {(revision.analysisRuns || []).length > 0 && ( - -
    - -
    -
    +
    + +
    )}
    )} @@ -96,185 +98,67 @@ export const RevisionWidget = (props: RevisionWidgetProps) => { ); }; -const AnalysisRunWidget = (props: {analysisRuns: RolloutAnalysisRunInfo[]}) => { - const {analysisRuns} = props; - const [selection, setSelection] = React.useState(null); +const analysisName = (ar: RolloutAnalysisRunInfo): string => { + const temp = ar.objectMeta?.name?.split('-') ?? ''; + const len = temp.length; + return len < 2 ? 'Analysis' : `Analysis ${temp[len - 2] + '-' + temp[len - 1]}`; +}; + +interface AnalysisRunWidgetProps { + analysisRuns: RolloutAnalysisRunInfo[]; + images: ImageInfo[]; + revision: string; +} + +const AnalysisRunWidget = ({analysisRuns, images, revision}: AnalysisRunWidgetProps) => { + const [selectedAnalysis, setSelectedAnalysis] = React.useState(null); + const imageNames = images.map((img) => img.image); return (
    Analysis Runs
    - {analysisRuns.map((ar) => { - let temp = ar.objectMeta.name.split('-'); - let len = temp.length; - return ( - -
    - Name: {ar.objectMeta.name} -
    -
    - Created at: - {formatTimestamp(JSON.stringify(ar.objectMeta?.creationTimestamp))} -
    -
    - Status: - {ar.status} -
    - - } - > -
    - -
    -
    - ); - })} -
    - - {selection && ( - -
    - {selection.objectMeta?.name} - -
    - {selection?.jobs && ( -
    -
    - {selection.jobs.map((job) => { - return ( - -
    job-name: {job.objectMeta?.name}
    -
    StartedAt: {formatTimestamp(JSON.stringify(job.startedAt))}
    -
    Status: {job.status}
    -
    MetricName: {job.metricName}
    -
    - } - customIcon={faChartBar} - /> - ); - })} -
    - metric.name === selection.jobs[0].metricName) - .map((metric) => { - return ( - - {metric?.name && ( -
    - MetricName: {metric.name} -
    - )} - {metric?.successCondition && ( -
    - SuccessCondition: - {metric.successCondition} -
    - )} - {metric?.failureLimit && ( -
    - FailureLimit: {metric.failureLimit} -
    - )} - {metric?.inconclusiveLimit && ( -
    - InconclusiveLimit: - {metric.inconclusiveLimit} -
    - )} - {metric?.count && ( -
    - Count: - {metric.count} -
    - )} -
    - ); - })} - > - -
    -
    - )} - {selection?.nonJobInfo && ( -
    -
    - {selection.nonJobInfo.map((nonJob) => { - return ( - -
    Value: {JSON.stringify(JSON.parse(nonJob.value), null, 2)}
    -
    StartedAt: {formatTimestamp(JSON.stringify(nonJob.startedAt))}
    -
    Status: {nonJob.status}
    -
    MetricName: {nonJob.metricName}
    -
    - } - customIcon={faChartBar} - /> - ); - })} -
    - metric.name === selection.nonJobInfo[0].metricName) - .map((metric) => { - return ( - - {metric?.name && ( -
    - MetricName: {metric.name} -
    - )} - {metric?.successCondition && ( -
    - SuccessCondition: - {metric.successCondition} -
    - )} - {metric?.failureLimit && ( -
    - FailureLimit: {metric.failureLimit} -
    - )} - {metric?.inconclusiveLimit && ( -
    - InconclusiveLimit: - {metric.inconclusiveLimit} -
    - )} - {metric?.count && ( -
    - Count: - {metric.count} -
    - )} -
    - ); - })} - > - -
    + {analysisRuns.map((ar) => ( + +
    + Name: {ar.objectMeta.name} +
    +
    + Created at: + {formatTimestamp(JSON.stringify(ar.objectMeta?.creationTimestamp))} +
    +
    + Status: + {ar.status} +
    + + }> +
    +
    - )} - +
    + ))} +
    + {selectedAnalysis !== null && ( + ar.objectMeta.name === selectedAnalysis.objectMeta.name)} + analysisName={analysisName(selectedAnalysis)} + images={imageNames} + revision={revision} + open={selectedAnalysis !== null} + onClose={() => setSelectedAnalysis(null)} + /> )}
    ); diff --git a/ui/src/app/components/rollout/rollout.tsx b/ui/src/app/components/rollout/rollout.tsx index 317d223d2d..f26b22a9a7 100644 --- a/ui/src/app/components/rollout/rollout.tsx +++ b/ui/src/app/components/rollout/rollout.tsx @@ -159,7 +159,6 @@ export const RolloutWidget = (props: {rollout: RolloutRolloutInfo; interactive?: initCollapsed={false} rollback={interactive ? (r) => interactive.api.rolloutServiceUndoRollout({}, interactive.namespace, rollout.objectMeta.name, `${r}`) : null} current={i === 0} - message={rollout.message} /> ))}
    diff --git a/ui/src/app/components/ticker/ticker.tsx b/ui/src/app/components/ticker/ticker.tsx new file mode 100644 index 0000000000..6b07fc9cca --- /dev/null +++ b/ui/src/app/components/ticker/ticker.tsx @@ -0,0 +1,40 @@ +import * as moment from 'moment'; +import * as React from 'react'; +import {interval, Subscription} from 'rxjs'; + +export class Ticker extends React.Component<{intervalMs?: number; disabled?: boolean; children?: (time: moment.Moment) => React.ReactNode}, {time: moment.Moment}> { + private subscription: Subscription | null = null; + + constructor(props: {intervalMs?: number; children?: (time: moment.Moment) => React.ReactNode}) { + super(props); + this.state = {time: moment()}; + this.ensureSubscribed(); + } + + public render() { + return this.props.children && this.props.children(this.state.time); + } + + public componentDidUpdate() { + this.ensureSubscribed(); + } + + public componentWillUnmount() { + this.ensureUnsubscribed(); + } + + private ensureSubscribed() { + if (this.props.disabled) { + this.ensureUnsubscribed(); + } else if (!this.subscription) { + this.subscription = interval(this.props.intervalMs || 1000).subscribe(() => this.setState({time: moment()})); + } + } + + private ensureUnsubscribed() { + if (this.subscription != null) { + this.subscription.unsubscribe(); + this.subscription = null; + } + } +} diff --git a/ui/src/config/theme.ts b/ui/src/config/theme.ts index c377896a2e..ef2390a25c 100644 --- a/ui/src/config/theme.ts +++ b/ui/src/config/theme.ts @@ -1,15 +1,18 @@ -import { ThemeConfig } from 'antd/es/config-provider'; +import {ThemeConfig} from 'antd/es/config-provider'; export const theme: ThemeConfig = { - components: { - Button: { - colorPrimary: '#44505f', - colorPrimaryBgHover: '#626f7e', - colorPrimaryHover: '#626f7e', - colorPrimaryActive: '#626f7e', - borderRadius: 100, - borderRadiusSM: 100, - borderRadiusLG: 100 - } - } + components: { + Button: { + colorPrimary: '#44505f', + colorPrimaryBgHover: '#626f7e', + colorPrimaryHover: '#626f7e', + colorPrimaryActive: '#626f7e', + borderRadius: 100, + borderRadiusSM: 100, + borderRadiusLG: 100, + }, + }, + token: { + colorPrimary: '#44505f', + }, }; diff --git a/ui/tsconfig.json b/ui/tsconfig.json index 26cd7a13fe..33aaf1dfd9 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -12,5 +12,6 @@ "allowSyntheticDefaultImports": true }, "include": ["./**/*"], - "exclude": ["node_modules", "./**/*.test.ts", "./**/*.test.tsx"] + "exclude": ["node_modules", "./**/*.test.tsx"], + "types": ["node", "jest"] } diff --git a/ui/yarn.lock b/ui/yarn.lock index 1db343cbbe..06695f961e 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -2,6 +2,14 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@ant-design/colors@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.0.tgz#eb7eecead124c3533aea05d61254f0a17f2b61b3" @@ -110,11 +118,24 @@ dependencies: "@babel/highlight" "^7.12.13" +"@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.4.tgz#03ae5af150be94392cb5c7ccd97db5a19a5da6aa" + integrity sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + "@babel/compat-data@^7.12.1", "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.14.0": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919" integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q== +"@babel/compat-data@^7.22.9": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.3.tgz#3febd552541e62b5e883a25eb3effd7c7379db11" + integrity sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ== + "@babel/core@7.12.3": version "7.12.3" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" @@ -158,6 +179,27 @@ semver "^6.3.0" source-map "^0.5.0" +"@babel/core@^7.11.6": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9" + integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.3" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.2" + "@babel/parser" "^7.23.3" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.3" + "@babel/types" "^7.23.3" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/generator@^7.12.1", "@babel/generator@^7.14.2": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.2.tgz#d5773e8b557d421fd6ce0d5efa5fd7fc22567c30" @@ -167,6 +209,16 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.23.3", "@babel/generator@^7.23.4", "@babel/generator@^7.7.2": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.4.tgz#4a41377d8566ec18f807f42962a7f3551de83d1c" + integrity sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ== + dependencies: + "@babel/types" "^7.23.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" @@ -192,6 +244,17 @@ browserslist "^4.14.5" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52" + integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.15" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.12.1", "@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.14.0": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.2.tgz#4e455b0329af29c2d3ad254b5dd5aed34595385d" @@ -226,6 +289,11 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-explode-assignable-expression@^7.12.13": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f" @@ -242,6 +310,14 @@ "@babel/template" "^7.12.13" "@babel/types" "^7.14.2" +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" @@ -257,6 +333,13 @@ "@babel/traverse" "^7.13.15" "@babel/types" "^7.13.16" +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72" @@ -271,6 +354,13 @@ dependencies: "@babel/types" "^7.13.12" +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + "@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.14.0", "@babel/helper-module-transforms@^7.14.2": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz#ac1cc30ee47b945e3e0c4db12fa0c5389509dfe5" @@ -285,6 +375,17 @@ "@babel/traverse" "^7.14.2" "@babel/types" "^7.14.2" +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" @@ -297,6 +398,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== +"@babel/helper-plugin-utils@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + "@babel/helper-remap-async-to-generator@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz#376a760d9f7b4b2077a9dd05aa9c3927cadb2209" @@ -323,6 +429,13 @@ dependencies: "@babel/types" "^7.13.12" +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.12.1": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" @@ -337,16 +450,38 @@ dependencies: "@babel/types" "^7.12.13" +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + "@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.0": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.17": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== +"@babel/helper-validator-option@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" + integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== + "@babel/helper-wrap-function@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz#bdb5c66fda8526ec235ab894ad53a1235c79fcc4" @@ -366,6 +501,15 @@ "@babel/traverse" "^7.14.0" "@babel/types" "^7.14.0" +"@babel/helpers@^7.23.2": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.4.tgz#7d2cfb969aa43222032193accd7329851facf3c1" + integrity sha512-HfcMizYz10cr3h29VqyfGL6ZWIjTwWfvYBMsBVGwpcbhNGe3wQ1ZXZRPzZoAHhd9OqHadHqjQ89iVKINXnbzuw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.4" + "@babel/types" "^7.23.4" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" @@ -375,11 +519,25 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.14.2", "@babel/parser@^7.7.0": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.2.tgz#0c1680aa44ad4605b16cbdcc5c341a61bde9c746" integrity sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ== +"@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.3", "@babel/parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.4.tgz#409fbe690c333bb70187e2de4021e1e47a026661" + integrity sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a" @@ -635,6 +793,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-syntax-jsx@^7.7.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -698,6 +863,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" +"@babel/plugin-syntax-typescript@^7.7.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" + integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz#10a59bebad52d637a027afa692e8d5ceff5e3dae" @@ -1281,6 +1453,15 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + "@babel/traverse@^7.1.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2", "@babel/traverse@^7.7.0": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b" @@ -1295,6 +1476,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.23.3", "@babel/traverse@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.4.tgz#c2790f7edf106d059a0098770fe70801417f3f85" + integrity sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg== + dependencies: + "@babel/code-frame" "^7.23.4" + "@babel/generator" "^7.23.4" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.4" + "@babel/types" "^7.23.4" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.6", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.16", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": version "7.14.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" @@ -1303,6 +1500,15 @@ "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" +"@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3", "@babel/types@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.4.tgz#7206a1810fc512a7f7f7d4dace4cb4c1c9dbfb8e" + integrity sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + 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" @@ -1469,6 +1675,18 @@ jest-util "^26.6.2" slash "^3.0.0" +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + "@jest/core@^26.6.0", "@jest/core@^26.6.3": version "26.6.3" resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" @@ -1503,6 +1721,40 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== + dependencies: + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + "@jest/environment@^26.6.0", "@jest/environment@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" @@ -1513,6 +1765,31 @@ "@types/node" "*" jest-mock "^26.6.2" +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== + dependencies: + expect "^29.7.0" + jest-snapshot "^29.7.0" + "@jest/fake-timers@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" @@ -1525,6 +1802,18 @@ jest-mock "^26.6.2" jest-util "^26.6.2" +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" + "@jest/globals@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" @@ -1534,6 +1823,16 @@ "@jest/types" "^26.6.2" expect "^26.6.2" +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" + "@jest/reporters@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" @@ -1566,6 +1865,43 @@ optionalDependencies: node-notifier "^8.0.0" +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^6.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" @@ -1575,6 +1911,15 @@ graceful-fs "^4.2.4" source-map "^0.6.0" +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@^26.6.0", "@jest/test-result@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" @@ -1585,6 +1930,16 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== + dependencies: + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + "@jest/test-sequencer@^26.6.3": version "26.6.3" resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" @@ -1596,6 +1951,16 @@ jest-runner "^26.6.3" jest-runtime "^26.6.3" +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== + dependencies: + "@jest/test-result" "^29.7.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + slash "^3.0.0" + "@jest/transform@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" @@ -1617,6 +1982,27 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + "@jest/types@^26.6.0", "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -1628,6 +2014,50 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -1818,6 +2248,11 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -1825,6 +2260,20 @@ dependencies: type-detect "4.0.8" +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" @@ -2015,6 +2464,17 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" +"@types/babel__core@^7.1.14": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__generator@*": version "7.6.2" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8" @@ -2042,6 +2502,57 @@ resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.9.tgz#d868b6febb02666330410fe7f58f3c4b8258be7b" integrity sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A== +"@types/d3-array@^3.0.3": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.9.tgz#54feabd29d1f15940d422c16008c63c1e4e3d188" + integrity sha512-mZowFN3p64ajCJJ4riVYlOjNlBJv3hctgAY01pjw3qTnJePD8s9DZmYDzhHKvzfCYvdjwylkU38+Vdt7Cu2FDA== + +"@types/d3-color@*": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.2.tgz#7939eed011a908287cd1bcfd11580c17b2ac7f8a" + integrity sha512-At+Ski7dL8Bs58E8g8vPcFJc8tGcaC12Z4m07+p41+DRqnZQcAlp3NfYjLrhNYv+zEyQitU1CUxXNjqUyf+c0g== + +"@types/d3-ease@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.1.tgz#ef386d2f28602dba82206888047f97f7f7f7558a" + integrity sha512-VZofjpEt8HWv3nxUAosj5o/+4JflnJ7Bbv07k17VO3T2WRuzGdZeookfaF60iVh5RdhVG49LE5w6LIshVUC6rg== + +"@types/d3-interpolate@^3.0.1": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.3.tgz#e10c06c4bf11bd770ed56184a0d76cd516ff4ded" + integrity sha512-6OZ2EIB4lLj+8cUY7I/Cgn9Q+hLdA4DjJHYOQDiHL0SzqS1K9DL5xIOVBSIHgF+tiuO9MU1D36qvdIvRDRPh+Q== + dependencies: + "@types/d3-color" "*" + +"@types/d3-path@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.0.1.tgz#6171c9e388904014764661a37613e3c4ab8df22d" + integrity sha512-blRhp7ki7pVznM8k6lk5iUU9paDbVRVq+/xpf0RRgSJn5gr6SE7RcFtxooYGMBOc1RZiGyqRpVdu5AD0z0ooMA== + +"@types/d3-scale@^4.0.2": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.6.tgz#9d221949f37b90b52696ec99f9b1e972d55fe10d" + integrity sha512-lo3oMLSiqsQUovv8j15X4BNEDOsnHuGjeVg7GRbAuB2PUa1prK5BNSOu6xixgNf3nqxPl4I1BqJWrPvFGlQoGQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-shape@^3.1.0": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.4.tgz#748a256d5e499cdfb3e48beca9c557f3ea0ff15c" + integrity sha512-M2/xsWPsjaZc5ifMKp1EBp0gqJG0eO/zlldJNOC85Y/5DGsBQ49gDkRJ2h5GY7ZVD6KUumvZWsylSbvTaJTqKg== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time@*", "@types/d3-time@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.2.tgz#f4425b2ebcb04495a7b2390da03633ef1a8adbe5" + integrity sha512-kbdRXTmUgNfw5OTE3KZnFQn6XdIc4QGroN5UixgdrXATmYsdlPQS6pEut9tVlIojtzuFD4txs/L+Rq41AHtLpg== + +"@types/d3-timer@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.1.tgz#8dac23292df0e559a3aa459d8efca78a734c3fbe" + integrity sha512-GGTvzKccVEhxmRfJEB6zhY9ieT4UhGVUIQaBzFpUO9OXy2ycAlnPCSJLzmGGgqt3KVjqN3QCQB4g1rsZnHsWhg== + "@types/eslint@^7.2.6": version "7.2.10" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.10.tgz#4b7a9368d46c0f8cd5408c23288a59aa2394d917" @@ -2075,6 +2586,13 @@ dependencies: "@types/node" "*" +"@types/graceful-fs@^4.1.3": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== + dependencies: + "@types/node" "*" + "@types/history@*": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" @@ -2090,26 +2608,31 @@ resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#3c9ee980f1a10d6021ae6632ca3e79ca2ec4fb50" integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== + +"@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" integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" - integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*", "@types/jest@^26.0.15": +"@types/jest@*": version "26.0.23" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.23.tgz#a1b7eab3c503b80451d019efb588ec63522ee4e7" integrity sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA== @@ -2117,6 +2640,14 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" +"@types/jest@^29.5.10": + version "29.5.10" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.10.tgz#a10fc5bab9e426081c12b2ef73d24d4f0c9b7f50" + integrity sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" @@ -2133,9 +2664,11 @@ integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA== "@types/node@*": - version "15.0.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.3.tgz#ee09fcaac513576474c327da5818d421b98db88a" - integrity sha512-/WbxFeBU+0F79z9RdEOXH4CsDga+ibi5M8uEYr91u3CkT/pdWcV8MCook+4wDPnZBexRdwWS+PiVZ2xJviAzcQ== + version "20.10.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.0.tgz#16ddf9c0a72b832ec4fcce35b8249cf149214617" + integrity sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ== + dependencies: + undici-types "~5.26.4" "@types/node@^12.0.0": version "12.20.13" @@ -2283,14 +2816,21 @@ source-map "^0.6.0" "@types/yargs-parser@*": - version "20.2.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" - integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^15.0.0": - version "15.0.13" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" - integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== + version "15.0.19" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.19.tgz#328fb89e46109ecbdb70c295d96ff2f46dfd01b9" + integrity sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== dependencies: "@types/yargs-parser" "*" @@ -2712,10 +3252,10 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.0, ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" @@ -2731,6 +3271,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + antd@^5.4.2: version "5.4.2" resolved "https://registry.yarnpkg.com/antd/-/antd-5.4.2.tgz#3923b96da76fc7276992e9fc0286ebb3a638e016" @@ -3145,6 +3690,19 @@ babel-jest@^26.6.0, babel-jest@^26.6.3: graceful-fs "^4.2.4" slash "^3.0.0" +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== + dependencies: + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + babel-loader@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" @@ -3174,6 +3732,17 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + babel-plugin-jest-hoist@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" @@ -3184,6 +3753,16 @@ babel-plugin-jest-hoist@^26.6.2: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-macros@2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" @@ -3266,6 +3845,14 @@ babel-preset-jest@^26.6.2: babel-plugin-jest-hoist "^26.6.2" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + babel-preset-react-app@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-10.0.0.tgz#689b60edc705f8a70ce87f47ab0e560a317d7045" @@ -3439,7 +4026,7 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -3538,6 +4125,23 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4 escalade "^3.1.1" node-releases "^1.1.71" +browserslist@^4.21.9: + version "4.22.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" + integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== + dependencies: + caniuse-lite "^1.0.30001541" + electron-to-chromium "^1.4.535" + node-releases "^2.0.13" + update-browserslist-db "^1.0.13" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -3713,6 +4317,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, can resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001228.tgz#bfdc5942cd3326fa51ee0b42fbef4da9d492a7fa" integrity sha512-QQmLOGJ3DEgokHbMSA8cj2a+geXqmnpyOFT0lhQV6P3/YOJvGDEwoedcwxEQ30gJIwIIunHIicunJ2rzK5gB2A== +caniuse-lite@^1.0.30001541: + version "1.0.30001565" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz#a528b253c8a2d95d2b415e11d8b9942acc100c4f" + integrity sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -3747,7 +4356,15 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== @@ -3819,6 +4436,11 @@ ci-info@^2.0.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +ci-info@^3.2.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -3837,6 +4459,11 @@ cjs-module-lexer@^0.6.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== +cjs-module-lexer@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -3887,6 +4514,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + 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" @@ -4111,6 +4747,11 @@ convert-source-map@^0.3.3: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -4258,6 +4899,19 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -4535,6 +5189,77 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= +"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6: + version "3.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +"d3-color@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-ease@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +"d3-format@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641" + integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA== + +"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +d3-path@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-scale@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +d3-shape@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4": + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +d3-timer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -4595,6 +5320,11 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js-light@^2.4.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + decimal.js@^10.2.1: version "10.2.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" @@ -4610,6 +5340,11 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== + deep-diff@^0.3.5: version "0.3.8" resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84" @@ -4733,6 +5468,11 @@ diff-sequences@^26.6.2: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -4808,6 +5548,13 @@ dom-converter@^0.2: dependencies: utila "~0.4" +dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" + dom-scroll-into-view@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dom-scroll-into-view/-/dom-scroll-into-view-1.0.1.tgz#32abb92f0d8feca6215162aef43e4b449ab8d99c" @@ -4926,6 +5673,11 @@ electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.723: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf" integrity sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg== +electron-to-chromium@^1.4.535: + version "1.4.596" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.596.tgz#6752d1aa795d942d49dfc5d3764d6ea283fab1d7" + integrity sha512-zW3zbZ40Icb2BCWjm47nxwcFGYlIgdXkAx85XDO7cyky9J4QQfq8t0W19/TLZqq3JPQXtlv8BPIGmfa9Jb4scg== + elliptic@^6.5.3: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -4939,6 +5691,11 @@ elliptic@^6.5.3: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + emittery@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" @@ -5386,7 +6143,7 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -eventemitter3@^4.0.0: +eventemitter3@^4.0.0, eventemitter3@^4.0.1: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -5489,6 +6246,17 @@ expect@^26.6.0, expect@^26.6.2: jest-message-util "^26.6.2" jest-regex-util "^26.0.0" +expect@^29.0.0, expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -5581,6 +6349,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-equals@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.0.1.tgz#a4eefe3c5d1c0d021aeed0bc10ba5e0c12ee405d" + integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ== + fast-glob@^3.1.1, fast-glob@^3.2.4: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" @@ -5593,7 +6366,7 @@ fast-glob@^3.1.1, fast-glob@^3.2.4: micromatch "^4.0.2" picomatch "^2.2.1" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -5885,11 +6658,21 @@ fsevents@^2.1.2, fsevents@^2.1.3, fsevents@~2.3.1: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -5900,7 +6683,7 @@ gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -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== @@ -6065,6 +6848,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -6183,6 +6971,13 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -6562,6 +7357,11 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + interpret@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" @@ -6692,6 +7492,13 @@ is-core-module@^2.0.0, is-core-module@^2.2.0: dependencies: has "^1.0.3" +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -6970,6 +7777,11 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== +istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" @@ -6980,6 +7792,28 @@ istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-instrument@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" + integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" @@ -7006,6 +7840,14 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +istanbul-reports@^3.1.3: + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + jest-changed-files@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" @@ -7015,6 +7857,15 @@ jest-changed-files@^26.6.2: execa "^4.0.0" throat "^5.0.0" +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== + dependencies: + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + jest-circus@26.6.0: version "26.6.0" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-26.6.0.tgz#7d9647b2e7f921181869faae1f90a2629fd70705" @@ -7042,6 +7893,32 @@ jest-circus@26.6.0: stack-utils "^2.0.2" throat "^5.0.0" +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-cli@^26.6.0: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" @@ -7061,6 +7938,23 @@ jest-cli@^26.6.0: prompts "^2.0.1" yargs "^15.4.1" +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== + dependencies: + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" + jest-config@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" @@ -7085,6 +7979,34 @@ jest-config@^26.6.3: micromatch "^4.0.2" pretty-format "^26.6.2" +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-diff@^26.0.0, jest-diff@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" @@ -7095,6 +8017,16 @@ jest-diff@^26.0.0, jest-diff@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-docblock@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" @@ -7102,6 +8034,13 @@ jest-docblock@^26.0.0: dependencies: detect-newline "^3.0.0" +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== + dependencies: + detect-newline "^3.0.0" + jest-each@^26.6.0, jest-each@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" @@ -7113,6 +8052,17 @@ jest-each@^26.6.0, jest-each@^26.6.2: jest-util "^26.6.2" pretty-format "^26.6.2" +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + jest-environment-jsdom@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" @@ -7138,11 +8088,28 @@ jest-environment-node@^26.6.2: jest-mock "^26.6.2" jest-util "^26.6.2" +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jest-get-type@^26.3.0: version "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-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -7164,6 +8131,25 @@ jest-haste-map@^26.6.2: optionalDependencies: fsevents "^2.1.2" +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== + dependencies: + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + jest-jasmine2@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" @@ -7196,6 +8182,14 @@ jest-leak-detector@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-matcher-utils@^26.6.0, jest-matcher-utils@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" @@ -7206,6 +8200,16 @@ jest-matcher-utils@^26.6.0, jest-matcher-utils@^26.6.2: jest-get-type "^26.3.0" pretty-format "^26.6.2" +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-message-util@^26.6.0, jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -7221,6 +8225,21 @@ jest-message-util@^26.6.0, jest-message-util@^26.6.2: slash "^3.0.0" stack-utils "^2.0.2" +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" @@ -7229,6 +8248,15 @@ jest-mock@^26.6.2: "@jest/types" "^26.6.2" "@types/node" "*" +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" @@ -7239,6 +8267,11 @@ 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-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== + jest-resolve-dependencies@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" @@ -7248,6 +8281,14 @@ jest-resolve-dependencies@^26.6.3: jest-regex-util "^26.0.0" jest-snapshot "^26.6.2" +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + jest-resolve@26.6.0: version "26.6.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.0.tgz#070fe7159af87b03e50f52ea5e17ee95bbee40e1" @@ -7276,6 +8317,21 @@ jest-resolve@^26.6.2: resolve "^1.18.1" slash "^3.0.0" +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" + jest-runner@^26.6.0, jest-runner@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" @@ -7302,6 +8358,33 @@ jest-runner@^26.6.0, jest-runner@^26.6.3: source-map-support "^0.5.6" throat "^5.0.0" +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.13.1" + graceful-fs "^4.2.9" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" + jest-runtime@^26.6.0, jest-runtime@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" @@ -7335,6 +8418,34 @@ jest-runtime@^26.6.0, jest-runtime@^26.6.3: strip-bom "^4.0.0" yargs "^15.4.1" +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" + jest-serializer@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" @@ -7365,6 +8476,32 @@ jest-snapshot@^26.6.0, jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" + jest-util@^26.6.0, jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" @@ -7377,6 +8514,18 @@ jest-util@^26.6.0, jest-util@^26.6.2: is-ci "^2.0.0" micromatch "^4.0.2" +jest-util@^29.0.0, jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" @@ -7389,6 +8538,18 @@ jest-validate@^26.6.2: leven "^3.1.0" pretty-format "^26.6.2" +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== + dependencies: + "@jest/types" "^29.6.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" + jest-watch-typeahead@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.6.1.tgz#45221b86bb6710b7e97baaa1640ae24a07785e63" @@ -7415,6 +8576,20 @@ jest-watcher@^26.3.0, jest-watcher@^26.6.2: jest-util "^26.6.2" string-length "^4.0.1" +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + jest-worker@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" @@ -7432,6 +8607,16 @@ jest-worker@^26.5.0, jest-worker@^26.6.2: merge-stream "^2.0.0" supports-color "^7.0.0" +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== + dependencies: + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest@26.6.0: version "26.6.0" resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.0.tgz#546b25a1d8c888569dbbe93cae131748086a4a25" @@ -7441,6 +8626,16 @@ jest@26.6.0: import-local "^3.0.2" jest-cli "^26.6.0" +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -7562,6 +8757,11 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -7766,7 +8966,7 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.memoize@^4.1.2: +lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= @@ -7861,6 +9061,18 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-error@1.x: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -7972,6 +9184,14 @@ micromatch@^4.0.0, micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.2.3" +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -8347,6 +9567,11 @@ node-releases@^1.1.61, node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -8639,7 +9864,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -8751,7 +9976,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -8829,6 +10054,11 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -8869,11 +10099,21 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -8903,6 +10143,11 @@ pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" +pirates@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + pkg-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" @@ -9670,6 +10915,15 @@ pretty-format@^26.0.0, pretty-format@^26.6.0, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -9801,6 +11055,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pure-rand@^6.0.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" + integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== + q@^1.1.2: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -10725,7 +11984,7 @@ react-hot-loader@^3.1.3: redbox-react "^1.3.6" source-map "^0.6.1" -react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -10735,7 +11994,7 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.2.0: +react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== @@ -10748,7 +12007,7 @@ react-keyhooks@^0.2.3: "@types/react" "^16.9.3" react "^16.9.3" -react-lifecycles-compat@^3.0.0: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -10778,6 +12037,13 @@ react-refresh@^0.8.3: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== +react-resize-detector@^8.0.4: + version "8.1.0" + resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-8.1.0.tgz#1c7817db8bc886e2dbd3fbe3b26ea8e56be0524a" + integrity sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w== + dependencies: + lodash "^4.17.21" + react-router-dom@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" @@ -10903,6 +12169,14 @@ react-side-effect@^2.1.0: resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.1.tgz#66c5701c3e7560ab4822a4ee2742dee215d72eb3" integrity sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ== +react-smooth@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-2.0.5.tgz#d153b7dffc7143d0c99e82db1532f8cf93f20ecd" + integrity sha512-BMP2Ad42tD60h0JW6BFaib+RJuV5dsXJK9Baxiv/HlNFjvRLqA9xrNKxVWnUIZPQfzUwGXIlU/dSYLU+54YGQA== + dependencies: + fast-equals "^5.0.0" + react-transition-group "2.9.0" + react-toastify@9.0.8: version "9.0.8" resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.0.8.tgz#3876c89fc6211a29027b3075010b5ec39ebe4f7e" @@ -10910,6 +12184,16 @@ react-toastify@9.0.8: dependencies: clsx "^1.1.1" +react-transition-group@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" + integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + react@^16.9.3: version "16.14.0" resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" @@ -11001,6 +12285,28 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +recharts-scale@^0.4.4: + version "0.4.5" + resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.5.tgz#0969271f14e732e642fcc5bd4ab270d6e87dd1d9" + integrity sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w== + dependencies: + decimal.js-light "^2.4.1" + +recharts@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.9.0.tgz#dde7531298cffe8677b1206967830d34f7972ea6" + integrity sha512-cVgiAU3W5UrA8nRRV/N0JrudgZzY/vjkzrlShbH+EFo1vs4nMlXgshZWLI0DfDLmn4/p4pF7Lq7F5PU+K94Ipg== + dependencies: + classnames "^2.2.5" + eventemitter3 "^4.0.1" + lodash "^4.17.19" + react-is "^16.10.2" + react-resize-detector "^8.0.4" + react-smooth "^2.0.4" + recharts-scale "^0.4.4" + tiny-invariant "^1.3.1" + victory-vendor "^36.6.8" + rechoir@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca" @@ -11297,6 +12603,11 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve.exports@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== + resolve@1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" @@ -11313,6 +12624,15 @@ resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.1 is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" @@ -11602,6 +12922,11 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" @@ -11609,6 +12934,13 @@ semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: dependencies: lru-cache "^6.0.0" +semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -11758,6 +13090,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -11875,6 +13212,14 @@ source-map-resolve@^0.6.0: atob "^2.1.2" decode-uri-component "^0.2.0" +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@^0.5.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" @@ -12022,6 +13367,13 @@ stack-utils@^2.0.2: dependencies: escape-string-regexp "^2.0.0" +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + stackframe@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4" @@ -12123,6 +13475,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.matchall@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz#608f255e93e072107f5de066f81a2dfb78cf6b29" @@ -12196,6 +13557,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -12279,6 +13647,13 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-hyperlinks@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" @@ -12287,6 +13662,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + svg-parser@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" @@ -12474,6 +13854,11 @@ tiny-invariant@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== +tiny-invariant@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== + tiny-warning@^1.0.0, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" @@ -12486,6 +13871,11 @@ tippy.js@^5.1.1: dependencies: popper.js "^1.16.0" +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -12582,6 +13972,20 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-jest@^29.1.1: + version "29.1.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" + integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "4.x" + make-error "1.x" + semver "^7.5.3" + yargs-parser "^21.0.1" + ts-loader@^8.0.17: version "8.2.0" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.2.0.tgz#6a3aeaa378aecda543e2ed2c332d3123841d52e0" @@ -12741,6 +14145,11 @@ unbox-primitive@^1.0.0: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -12838,6 +14247,14 @@ upath@^1.1.1, upath@^1.1.2, upath@^1.2.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -12956,6 +14373,15 @@ v8-to-istanbul@^7.0.0: convert-source-map "^1.6.0" source-map "^0.7.3" +v8-to-istanbul@^9.0.1: + version "9.2.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" + integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^2.0.0" + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -12988,6 +14414,26 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +victory-vendor@^36.6.8: + version "36.6.11" + resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.6.11.tgz#acae770717c2dae541a54929c304ecab5ab6ac2a" + integrity sha512-nT8kCiJp8dQh8g991J/R5w5eE2KnO8EAIP0xocWlh9l2okngMWglOPoMZzJvek8Q1KUc4XE/mJxTZnvOB1sTYg== + dependencies: + "@types/d3-array" "^3.0.3" + "@types/d3-ease" "^3.0.0" + "@types/d3-interpolate" "^3.0.1" + "@types/d3-scale" "^4.0.2" + "@types/d3-shape" "^3.1.0" + "@types/d3-time" "^3.0.0" + "@types/d3-timer" "^3.0.0" + d3-array "^3.1.6" + d3-ease "^3.0.1" + d3-interpolate "^3.0.1" + d3-scale "^4.0.2" + d3-shape "^3.1.0" + d3-time "^3.0.0" + d3-timer "^3.0.1" + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -13014,6 +14460,13 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + warning@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" @@ -13525,6 +14978,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" @@ -13540,6 +15002,14 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" @@ -13582,6 +15052,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" @@ -13613,6 +15088,11 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^21.0.1, yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs@^13.3.2: version "13.3.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" @@ -13646,6 +15126,19 @@ yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"