[Synthetics] Sorting and infinite scroll (#139254)

* unskip monitor state scoping tests

* add describe.only

* unskip monitor state scoping test

* add retry logic

* add sort order

* add alphabetical and modified on sorting

* adjust styling

* adjust overview state

* add infinite scroll and sort by status

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* adjust test

* adjust tests

* adjust tests

* adjust ux

* update infinite scroll behavior

* adjust ux

* adjust test

* adjust i18n

* Update x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx

* Update x-pack/plugins/synthetics/public/apps/synthetics/hooks/use_monitors_sorted_by_status.tsx

* adjust hook

* update scroll logic

* adjust sort field markup

* fix/synthetics-preserve-id-field-on-monitor-attributes

* add overview sorting e2e tests

* add scroll test

* adjust tests

* adjust synthetics version

* ensure test monitors are cleaned up

* remove monitor id from use_monitors_sorted_by_status hook

* remove unnecessary comments

* update code formatting

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* adjust overview sorting logic

* adjust tests

* Update x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_scrolling.journey.ts

* adjust test

* fix duplicate overview request

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Dominique Clarke 2022-11-04 13:19:19 -04:00 committed by GitHub
parent 74f6554b51
commit f1f1b5789d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 1799 additions and 351 deletions

View file

@ -708,7 +708,7 @@
"@cypress/webpack-preprocessor": "^5.12.2",
"@elastic/eslint-plugin-eui": "0.0.2",
"@elastic/makelogs": "^6.1.1",
"@elastic/synthetics": "^1.0.0-beta.22",
"@elastic/synthetics": "^1.0.0-beta.23",
"@emotion/babel-preset-css-prop": "^11.10.0",
"@emotion/jest": "^11.10.0",
"@istanbuljs/nyc-config-typescript": "^1.0.2",
@ -1089,7 +1089,7 @@
"pirates": "^4.0.1",
"piscina": "^3.2.0",
"pixelmatch": "^5.3.0",
"playwright": "^1.17.1",
"playwright": "^1.26.0",
"pngjs": "^3.4.0",
"postcss": "^8.4.14",
"postcss-loader": "^4.2.0",

View file

@ -400,7 +400,7 @@ export type MonitorOverviewItem = t.TypeOf<typeof MonitorOverviewItemCodec>;
export const MonitorOverviewResultCodec = t.type({
total: t.number,
allMonitorIds: t.array(t.string),
pages: t.record(t.string, t.array(MonitorOverviewItemCodec)),
monitors: t.array(MonitorOverviewItemCodec),
});
export type MonitorOverviewResult = t.TypeOf<typeof MonitorOverviewResultCodec>;

View file

@ -7,10 +7,19 @@
import * as t from 'io-ts';
export const OverviewStatusMetaDataCodec = t.interface({
heartbeatId: t.string,
configId: t.string,
location: t.string,
});
export const OverviewStatusType = t.type({
up: t.number,
down: t.number,
disabledCount: t.number,
upConfigs: t.array(OverviewStatusMetaDataCodec),
downConfigs: t.array(OverviewStatusMetaDataCodec),
});
export type OverviewStatus = t.TypeOf<typeof OverviewStatusType>;
export type OverviewStatusMetaData = t.TypeOf<typeof OverviewStatusMetaDataCodec>;

View file

@ -8,3 +8,5 @@
export * from './getting_started.journey';
export * from './add_monitor.journey';
export * from './monitor_selector.journey';
export * from './overview_sorting.journey';
export * from './overview_scrolling.journey';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { journey, step, expect, before } from '@elastic/synthetics';
import { journey, step, expect, before, after } from '@elastic/synthetics';
import {
addTestMonitor,
cleanTestMonitors,
@ -28,6 +28,10 @@ journey(`MonitorSelector`, async ({ page, params }) => {
await addTestMonitor(params.kibanaUrl, testMonitor3);
});
after(async () => {
await cleanTestMonitors(params);
});
step('Go to monitor-management', async () => {
await syntheticsApp.navigateToMonitorManagement();
});

View file

@ -0,0 +1,63 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { before, after, expect, journey, step } from '@elastic/synthetics';
import {
addTestMonitor,
cleanTestMonitors,
enableMonitorManagedViaApi,
} from './services/add_monitor';
import { syntheticsAppPageProvider } from '../../page_objects/synthetics_app';
journey('Overview Scrolling', async ({ page, params }) => {
const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl });
before(async () => {
await enableMonitorManagedViaApi(params.kibanaUrl);
await cleanTestMonitors(params);
for (let i = 0; i < 100; i++) {
await addTestMonitor(params.kibanaUrl, `test monitor ${i}`);
}
await syntheticsApp.waitForLoadingToFinish();
});
after(async () => {
await cleanTestMonitors(params);
});
step('Go to overview', async () => {
await syntheticsApp.navigateToOverview();
});
step('login to Kibana', async () => {
await syntheticsApp.loginToKibana();
const invalid = await page.locator(`text=Username or password is incorrect. Please try again.`);
expect(await invalid.isVisible()).toBeFalsy();
});
step('scroll until you see showing all monitors', async () => {
let showingAllMonitorsNode;
const gridItems = await page.locator(`[data-test-subj="syntheticsOverviewGridItem"]`);
await page.waitForSelector(`text="test monitor 0"`);
let count = await gridItems.count();
expect(count).toBe(32);
while (!showingAllMonitorsNode) {
await page.mouse.wheel(0, 100);
showingAllMonitorsNode = await page.$(`text="Showing all monitors"`);
}
expect(await showingAllMonitorsNode.isVisible()).toBe(true);
count = await gridItems.count();
expect(count).toBe(100);
});
});

View file

@ -0,0 +1,130 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { before, expect, journey, step } from '@elastic/synthetics';
import {
addTestMonitor,
cleanTestMonitors,
enableMonitorManagedViaApi,
} from './services/add_monitor';
import { syntheticsAppPageProvider } from '../../page_objects/synthetics_app';
journey('Overview Sorting', async ({ page, params }) => {
const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl });
const testMonitor1 = 'acb'; // second alpha, first created
const testMonitor2 = 'aCd'; // third alpha, second created
const testMonitor3 = 'Abc'; // first alpha, last created
before(async () => {
await enableMonitorManagedViaApi(params.kibanaUrl);
await cleanTestMonitors(params);
await addTestMonitor(params.kibanaUrl, testMonitor1);
await addTestMonitor(params.kibanaUrl, testMonitor2);
await addTestMonitor(params.kibanaUrl, testMonitor3);
await syntheticsApp.waitForLoadingToFinish();
});
step('Go to monitor-management', async () => {
await syntheticsApp.navigateToOverview();
});
step('login to Kibana', async () => {
await syntheticsApp.loginToKibana();
const invalid = await page.locator(`text=Username or password is incorrect. Please try again.`);
expect(await invalid.isVisible()).toBeFalsy();
});
step('sort alpbhaetical asc', async () => {
await syntheticsApp.navigateToOverview();
await page.waitForSelector(`[data-test-subj="syntheticsOverviewGridItem"]`);
await page.click('[data-test-subj="syntheticsOverviewSortButton"]');
await page.click('button:has-text("Alphabetical")');
await page.waitForSelector('text=Loading');
await page.waitForSelector(`text=${testMonitor1}`);
await page.waitForSelector(`text=${testMonitor2}`);
await page.waitForSelector(`text=${testMonitor3}`);
const gridItems = await page.locator(`[data-test-subj="syntheticsOverviewGridItem"]`);
const first = await gridItems.nth(0);
const second = await gridItems.nth(1);
const third = await gridItems.nth(2);
const correctFirstMonitor = await first.locator(`button:has-text('${testMonitor3}')`);
const correctSecondMonitor = await second.locator(`button:has-text('${testMonitor1}')`);
const correctThirdMonitor = await third.locator(`button:has-text('${testMonitor2}')`);
expect(await correctFirstMonitor.count()).toBe(1);
expect(await correctSecondMonitor.count()).toBe(1);
expect(await correctThirdMonitor.count()).toBe(1);
});
step('sort alpbhaetical desc', async () => {
await page.waitForSelector(`[data-test-subj="syntheticsOverviewGridItem"]`);
await page.click('[data-test-subj="syntheticsOverviewSortButton"]');
await page.click('button:has-text("Z -> A")');
await page.waitForSelector('text=Loading');
await page.waitForSelector(`text=${testMonitor1}`);
await page.waitForSelector(`text=${testMonitor2}`);
await page.waitForSelector(`text=${testMonitor3}`);
const gridItems = await page.locator(`[data-test-subj="syntheticsOverviewGridItem"]`);
const first = await gridItems.nth(0);
const second = await gridItems.nth(1);
const third = await gridItems.nth(2);
const correctFirstMonitor = await first.locator(`button:has-text('${testMonitor2}')`);
const correctSecondMonitor = await second.locator(`button:has-text('${testMonitor1}')`);
const correctThirdMonitor = await third.locator(`button:has-text('${testMonitor3}')`);
expect(await correctFirstMonitor.count()).toBe(1);
expect(await correctSecondMonitor.count()).toBe(1);
expect(await correctThirdMonitor.count()).toBe(1);
});
step('sort last updated asc', async () => {
await page.waitForSelector(`[data-test-subj="syntheticsOverviewGridItem"]`);
await page.click('[data-test-subj="syntheticsOverviewSortButton"]');
await page.click('button:has-text("Last modified")');
await page.waitForSelector('text=Loading');
await page.waitForSelector(`text=${testMonitor1}`);
await page.waitForSelector(`text=${testMonitor2}`);
await page.waitForSelector(`text=${testMonitor3}`);
const gridItems = await page.locator(`[data-test-subj="syntheticsOverviewGridItem"]`);
const first = await gridItems.nth(0);
const second = await gridItems.nth(1);
const third = await gridItems.nth(2);
const correctFirstMonitor = await first.locator(`button:has-text('${testMonitor3}')`);
const correctSecondMonitor = await second.locator(`button:has-text('${testMonitor2}')`);
const correctThirdMonitor = await third.locator(`button:has-text('${testMonitor1}')`);
expect(await correctFirstMonitor.count()).toBe(1);
expect(await correctSecondMonitor.count()).toBe(1);
expect(await correctThirdMonitor.count()).toBe(1);
await page.waitForTimeout(30000);
});
step('sort last updated desc', async () => {
await page.waitForSelector(`[data-test-subj="syntheticsOverviewGridItem"]`);
await page.click('[data-test-subj="syntheticsOverviewSortButton"]');
await page.click('button:has-text("Oldest first")');
await page.waitForSelector('text=Loading');
await page.waitForSelector(`text=${testMonitor1}`);
await page.waitForSelector(`text=${testMonitor2}`);
await page.waitForSelector(`text=${testMonitor3}`);
const gridItems = await page.locator(`[data-test-subj="syntheticsOverviewGridItem"]`);
const first = await gridItems.nth(0);
const second = await gridItems.nth(1);
const third = await gridItems.nth(2);
const correctFirstMonitor = await first.locator(`button:has-text('${testMonitor1}')`);
const correctSecondMonitor = await second.locator(`button:has-text('${testMonitor2}')`);
const correctThirdMonitor = await third.locator(`button:has-text('${testMonitor3}')`);
expect(await correctFirstMonitor.count()).toBe(1);
expect(await correctSecondMonitor.count()).toBe(1);
expect(await correctThirdMonitor.count()).toBe(1);
await page.waitForTimeout(30000);
});
step('delete monitors', async () => {
await syntheticsApp.navigateToMonitorManagement();
expect(await syntheticsApp.deleteMonitors()).toBeTruthy();
});
});

View file

@ -21,6 +21,7 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib
const basePath = isRemote ? remoteKibanaUrl : kibanaUrl;
const monitorManagement = `${basePath}/app/synthetics/monitors`;
const addMonitor = `${basePath}/app/synthetics/add-monitor`;
const overview = `${basePath}/app/synthetics`;
return {
...loginPageProvider({
page,
@ -37,6 +38,10 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib
await this.waitForMonitorManagementLoadingToFinish();
},
async navigateToOverview() {
await page.goto(overview, { waitUntil: 'networkidle' });
},
async waitForMonitorManagementLoadingToFinish() {
while (true) {
if ((await page.$(this.byTestId('uptimeLoader'))) === null) break;

View file

@ -7,13 +7,14 @@
import { i18n } from '@kbn/i18n';
import React, { useState } from 'react';
import { Chart, Settings, Metric, MetricTrendShape } from '@elastic/charts';
import { EuiPanel, EuiLoadingChart } from '@elastic/eui';
import { EuiPanel } from '@elastic/eui';
import { DARK_THEME } from '@elastic/charts';
import { useTheme } from '@kbn/observability-plugin/public';
import { useLocationName, useStatusByLocation } from '../../../../hooks';
import { formatDuration } from '../../../../utils/formatting';
import { MonitorOverviewItem, Ping } from '../../../../../../../common/runtime_types';
import { ActionsPopover } from './actions_popover';
import { OverviewGridItemLoader } from './overview_grid_item_loader';
export const getColor = (theme: ReturnType<typeof useTheme>, isEnabled: boolean, ping?: Ping) => {
if (!isEnabled) {
@ -101,7 +102,7 @@ export const MetricItem = ({
)}
</EuiPanel>
) : (
<EuiLoadingChart mono />
<OverviewGridItemLoader />
)}
</div>
);

View file

@ -48,22 +48,21 @@ describe('Overview Grid', () => {
return hits;
};
const perPage = 20;
it('renders correctly', async () => {
jest
.spyOn(hooks, 'useLast50DurationChart')
.mockReturnValue({ data: getMockChart(), averageDuration: 30000, loading: false });
const { getByText, getAllByTestId } = render(<OverviewGrid />, {
const { getByText, getAllByTestId, queryByText } = render(<OverviewGrid />, {
state: {
overview: {
pageState: {
perPage: 20,
perPage,
},
data: {
pages: {
0: getMockData().slice(0, 20),
1: getMockData().slice(20, 40),
},
monitors: getMockData(),
allMonitorIds: [], // not critical for this test
total: getMockData().length,
},
@ -87,9 +86,49 @@ describe('Overview Grid', () => {
},
});
expect(getByText(/1-20/)).toBeInTheDocument();
expect(getByText(/of 40/)).toBeInTheDocument();
expect(getByText('Rows per page: 20')).toBeInTheDocument();
expect(getAllByTestId('syntheticsOverviewGridItem').length).toEqual(20);
expect(getByText('Showing')).toBeInTheDocument();
expect(getByText('40')).toBeInTheDocument();
expect(getByText('Monitors')).toBeInTheDocument();
expect(queryByText('Showing all monitors')).not.toBeInTheDocument();
expect(getAllByTestId('syntheticsOverviewGridItem').length).toEqual(perPage);
});
it('displays showing all monitors label when reaching the end of the list', async () => {
jest
.spyOn(hooks, 'useLast50DurationChart')
.mockReturnValue({ data: getMockChart(), averageDuration: 30000, loading: false });
const { getByText } = render(<OverviewGrid />, {
state: {
overview: {
pageState: {
perPage,
},
data: {
monitors: getMockData().slice(0, 16),
allMonitorIds: [], // not critical for this test
total: getMockData().length,
},
loaded: true,
loading: false,
},
serviceLocations: {
locations: [
{
id: 'us_central',
label: 'Us Central',
},
{
id: 'us_east',
label: 'US East',
},
],
locationsLoaded: true,
loading: false,
},
},
});
expect(getByText('Showing all monitors')).toBeInTheDocument();
});
});

View file

@ -4,40 +4,77 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import React, { useEffect, useState, useRef } from 'react';
import { i18n } from '@kbn/i18n';
import useThrottle from 'react-use/lib/useThrottle';
import { useSelector } from 'react-redux';
import useIntersection from 'react-use/lib/useIntersection';
import {
EuiFlexGroup,
EuiFlexItem,
EuiFlexGrid,
EuiSpacer,
EuiTablePagination,
EuiFlexGroup,
EuiButtonEmpty,
EuiText,
} from '@elastic/eui';
import { selectOverviewState, setOverviewPerPageAction } from '../../../../state/overview';
import { selectOverviewState } from '../../../../state/overview';
import { MonitorOverviewItem } from '../../../../../../../common/runtime_types';
import { OverviewPaginationInfo } from './overview_pagination_info';
import { OverviewGridItem } from './overview_grid_item';
import { SortFields } from './sort_fields';
import { useMonitorsSortedByStatus } from '../../../../hooks/use_monitors_sorted_by_status';
import { OverviewLoader } from './overview_loader';
import { OverviewStatus } from './overview_status';
export const OverviewGrid = () => {
const {
data: { pages },
data: { monitors },
loaded,
pageState: { perPage },
pageState,
} = useSelector(selectOverviewState);
const dispatch = useDispatch();
const [page, setPage] = useState(0);
const currentMonitors = pages[page] || [];
const { perPage, sortField } = pageState;
const [loadNextPage, setLoadNextPage] = useState(false);
const [page, setPage] = useState(1);
const goToPage = (pageNumber: number) => {
setPage(pageNumber);
};
const { monitorsSortedByStatus } = useMonitorsSortedByStatus(
sortField === 'status' && monitors.length !== 0
);
const currentMonitors = getCurrentMonitors({
monitors,
monitorsSortedByStatus,
perPage,
page,
sortField,
});
const changeItemsPerPage = (itemsPerPage: number) => {
dispatch(setOverviewPerPageAction(itemsPerPage));
};
const intersectionRef = useRef(null);
const intersection = useIntersection(intersectionRef, {
root: null,
rootMargin: '640px', // Height of 4 rows of monitors, minus the gutters
threshold: 0.1,
});
const hasIntersected = intersection && intersection.intersectionRatio > 0;
return loaded ? (
useThrottle(() => {
if (
hasIntersected &&
currentMonitors.length === page * perPage &&
currentMonitors.length !== monitors.length
) {
setLoadNextPage(true);
} else {
setLoadNextPage(false);
}
}, 1000);
useEffect(() => {
if (loadNextPage) {
setPage((p) => p + 1);
setLoadNextPage(false);
}
}, [loadNextPage]);
return (
<>
<EuiFlexGroup gutterSize="none">
<EuiFlexItem grow={false}>
@ -45,29 +82,82 @@ export const OverviewGrid = () => {
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<OverviewPaginationInfo page={page} />
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<OverviewPaginationInfo page={page} loading={!loaded} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<SortFields onSortChange={() => setPage(1)} />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGrid columns={4}>
{currentMonitors.map((monitor) => (
<EuiFlexItem
key={`${monitor.id}-${monitor.location?.id}`}
data-test-subj="syntheticsOverviewGridItem"
>
<OverviewGridItem monitor={monitor} />
{loaded && currentMonitors.length ? (
<EuiFlexGrid columns={4} data-test-subj="syntheticsOverviewGridItemContainer">
{currentMonitors.map((monitor) => (
<EuiFlexItem
key={`${monitor.id}-${monitor.location?.id}`}
data-test-subj="syntheticsOverviewGridItem"
>
<OverviewGridItem monitor={monitor} />
</EuiFlexItem>
))}
</EuiFlexGrid>
) : (
<OverviewLoader />
)}
<span ref={intersectionRef}>
<EuiSpacer size="l" />
</span>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
{currentMonitors.length === monitors.length && (
<EuiFlexItem grow={false}>
<EuiText size="xs">{SHOWING_ALL_MONITORS_LABEL}</EuiText>
</EuiFlexItem>
))}
</EuiFlexGrid>
<EuiTablePagination
aria-label={i18n.translate('xpack.synthetics.overview.pagination.ariaLabel', {
defaultMessage: 'Pagination for monitor overview',
})}
pageCount={Object.keys(pages).length}
activePage={page}
onChangePage={goToPage}
itemsPerPage={perPage}
onChangeItemsPerPage={changeItemsPerPage}
itemsPerPageOptions={[10, 20, 40]}
/>
)}
{currentMonitors.length === monitors.length && currentMonitors.length > perPage && (
<EuiFlexItem grow={false}>
<EuiButtonEmpty
onClick={() => window.scrollTo(0, 0)}
iconType="sortUp"
iconSide="right"
size="xs"
>
{SCROLL_TO_TOP_LABEL}
</EuiButtonEmpty>
</EuiFlexItem>
)}
</EuiFlexGroup>
</>
) : null;
);
};
const getCurrentMonitors = ({
sortField,
perPage,
page,
monitors,
monitorsSortedByStatus,
}: {
sortField: string;
perPage: number;
page: number;
monitors: MonitorOverviewItem[];
monitorsSortedByStatus: MonitorOverviewItem[];
}) => {
if (sortField === 'status') {
return monitorsSortedByStatus.slice(0, perPage * page);
} else {
return monitors.slice(0, perPage * page);
}
};
const SHOWING_ALL_MONITORS_LABEL = i18n.translate(
'xpack.synthetics.overview.grid.showingAllMonitors.label',
{
defaultMessage: 'Showing all monitors',
}
);
const SCROLL_TO_TOP_LABEL = i18n.translate('xpack.synthetics.overview.grid.scrollToTop.label', {
defaultMessage: 'Back to top',
});

View file

@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiPanel, EuiLoadingContent } from '@elastic/eui';
export const OverviewGridItemLoader = () => {
return (
<EuiPanel
style={{
height: '160px',
}}
hasBorder={true}
>
<EuiLoadingContent lines={2} />
</EuiPanel>
);
};

View file

@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
import { OverviewGridItemLoader } from './overview_grid_item_loader';
export const OverviewLoader = () => {
const ROWS = 4;
const COLUMNS = 4;
const loaders = Array(ROWS * COLUMNS).fill(null);
return (
<>
<EuiFlexGrid columns={COLUMNS}>
{loaders.map((_, i) => (
<EuiFlexItem key={i}>
<OverviewGridItemLoader />
</EuiFlexItem>
))}
</EuiFlexGrid>
</>
);
};

View file

@ -5,42 +5,88 @@
* 2.0.
*/
import React from 'react';
import { EuiText } from '@elastic/eui';
import { EuiText, EuiLoadingSpinner, EuiI18nNumber, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { useSelector } from 'react-redux';
import { FormattedMessage } from '@kbn/i18n-react';
import { selectOverviewState } from '../../../../state/overview';
export const OverviewPaginationInfo = ({ page }: { page: number }) => {
export const OverviewPaginationInfo = ({
page,
loading,
startRange,
endRange,
}: {
page: number;
loading: boolean;
startRange?: number;
endRange?: number;
}) => {
const {
data: { total, pages },
data: { total, monitors },
loaded,
pageState: { perPage },
} = useSelector(selectOverviewState);
const startRange = (page + 1) * perPage - perPage + 1;
const endRange = startRange + (pages[`${page}`]?.length || 0) - 1;
if (loaded && !Object.keys(pages).length) {
if (loaded && !monitors.length) {
return null;
}
return loaded ? (
<EuiText size="xs">
<FormattedMessage
id="xpack.synthetics.overview.pagination.description"
defaultMessage="Showing {currentCount} of {total} {monitors}"
values={{
currentCount: <strong>{`${startRange}-${endRange}`}</strong>,
total,
monitors: (
<strong>
<FormattedMessage
id="xpack.synthetics.overview.monitors.label"
defaultMessage="Monitors"
/>
</strong>
),
}}
/>
{startRange && endRange ? (
<FormattedMessage
id="xpack.synthetics.overview.pagination.description"
defaultMessage="Showing {currentCount} of {total} {monitors}"
values={{
currentCount: <strong>{`${startRange}-${endRange}`}</strong>,
total,
monitors: (
<strong>
<FormattedMessage
id="xpack.synthetics.overview.monitors.label"
defaultMessage="Monitors"
/>
</strong>
),
}}
/>
) : (
<FormattedMessage
id="xpack.synthetics.management.monitorList.recordTotal"
defaultMessage="Showing {total} {monitorsLabel}"
values={{
total: (
<strong>
<EuiI18nNumber value={total} />
</strong>
),
monitorsLabel: (
<strong>
<FormattedMessage
id="xpack.synthetics.management.monitorList.recordRangeLabel"
defaultMessage="{monitorCount, plural, one {Monitor} other {Monitors}}"
values={{
monitorCount: total,
}}
/>
</strong>
),
}}
/>
)}
</EuiText>
) : null;
) : (
<EuiText size="xs">
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="m" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.synthetics.overview.pagination.loading"
defaultMessage="Loading Monitors..."
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiText>
);
};

View file

@ -0,0 +1,211 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import type { PayloadAction } from '@reduxjs/toolkit';
import { useDispatch, useSelector } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import { ConfigKey } from '../../../../../../../common/runtime_types';
import { selectOverviewState, setOverviewPageStateAction } from '../../../../state/overview';
import { SortMenu } from './sort_menu';
export const SortFields = ({ onSortChange }: { onSortChange?: () => void }) => {
const {
pageState: { sortOrder, sortField },
} = useSelector(selectOverviewState);
const dispatch = useDispatch();
const { asc, desc, label } = getOrderContent(sortField);
const handleSortChange = (payloadAction: PayloadAction<unknown>) => {
if (onSortChange) {
onSortChange();
}
dispatch(payloadAction);
};
const orderByOptions = [
{
label: asc,
value: 'asc',
checked: sortOrder === 'asc',
onClick: () => {
handleSortChange(
setOverviewPageStateAction({
sortOrder: 'asc',
})
);
},
},
{
label: desc,
value: 'desc',
checked: sortOrder === 'desc',
onClick: () => {
handleSortChange(
setOverviewPageStateAction({
sortOrder: 'desc',
})
);
},
},
];
const sortByOptions = [
{
label: STATUS_LABEL,
value: 'status',
checked: sortField === 'status',
defaultSortOrder: 'asc',
onClick: () => {
handleSortChange(
setOverviewPageStateAction({
sortField: 'status',
sortOrder: 'asc',
})
);
},
},
{
label: ALPHABETICAL_LABEL,
value: `${ConfigKey.NAME}.keyword`,
checked: sortField === `${ConfigKey.NAME}.keyword`,
defaultSortOrder: 'asc',
onClick: () => {
handleSortChange(
setOverviewPageStateAction({
sortField: `${ConfigKey.NAME}.keyword`,
sortOrder: 'asc',
})
);
},
},
{
label: LAST_MODIFIED_LABEL,
value: 'updated_at',
checked: sortField === 'updated_at',
defaultSortOrder: 'desc',
onClick: () => {
handleSortChange(
setOverviewPageStateAction({
sortField: 'updated_at',
sortOrder: 'desc',
})
);
},
},
];
return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexGroup responsive={false} gutterSize="none" alignItems="center">
<EuiFlexItem grow={false}>
<EuiTitle size="xxxs">
<span>{SORT_TITLE}</span>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false} data-test-subj="syntheticsOverviewSortButton">
<SortMenu sortOptions={sortByOptions} orderOptions={orderByOptions} sortField={label} />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};
const getOrderContent = (sortField: string) => {
switch (sortField) {
case `${ConfigKey.NAME}.keyword`:
return {
asc: SORT_ALPHABETICAL_ASC,
desc: SORT_ALPHABETICAL_DESC,
label: ALPHABETICAL_LABEL,
};
case 'updated_at':
return {
asc: SORT_UPDATED_ASC,
desc: SORT_UPDATED_DESC,
label: LAST_MODIFIED_LABEL,
};
case 'status':
return {
asc: SORT_STATUS_ASC,
desc: SORT_STATUS_DESC,
label: STATUS_LABEL,
};
default:
return {
asc: ASCENDING_LABEL,
desc: DESCENDING_LABEL,
label: '',
};
}
};
const SORT_TITLE = i18n.translate('xpack.synthetics.overview.sortPopover.sort.title', {
defaultMessage: 'Sort',
});
const SORT_ALPHABETICAL_ASC = i18n.translate(
'xpack.synthetics.overview.sortPopover.alphabetical.asc',
{
defaultMessage: 'A -> Z',
description: 'Describes ascending alphabetical sort order',
}
);
const SORT_ALPHABETICAL_DESC = i18n.translate(
'xpack.synthetics.overview.sortPopover.alphabetical.desc',
{
defaultMessage: 'Z -> A',
description: 'Describes descending alphabetical sort order',
}
);
const SORT_UPDATED_ASC = i18n.translate('xpack.synthetics.overview.sortPopover.lastModified.asc', {
defaultMessage: 'Oldest first',
});
const SORT_UPDATED_DESC = i18n.translate(
'xpack.synthetics.overview.sortPopover.lastModified.desc',
{
defaultMessage: 'Newest first',
}
);
const SORT_STATUS_ASC = i18n.translate('xpack.synthetics.overview.sortPopover.status.asc', {
defaultMessage: 'Down first',
});
const SORT_STATUS_DESC = i18n.translate('xpack.synthetics.overview.sortPopover.status.desc', {
defaultMessage: 'Up first',
});
const ASCENDING_LABEL = i18n.translate('xpack.synthetics.overview.sortPopover.ascending.label', {
defaultMessage: 'Ascending',
});
const DESCENDING_LABEL = i18n.translate('xpack.synthetics.overview.sortPopover.descending.label', {
defaultMessage: 'Descending',
});
const STATUS_LABEL = i18n.translate('xpack.synthetics.overview.sortPopover.status.label', {
defaultMessage: 'Status',
});
const ALPHABETICAL_LABEL = i18n.translate(
'xpack.synthetics.overview.sortPopover.alphabetical.label',
{
defaultMessage: 'Alphabetical',
}
);
const LAST_MODIFIED_LABEL = i18n.translate(
'xpack.synthetics.overview.sortPopover.lastModified.label',
{
defaultMessage: 'Last modified',
}
);

View file

@ -0,0 +1,126 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiButtonEmpty,
EuiContextMenuPanel,
EuiContextMenuItem,
EuiPopover,
useGeneratedHtmlId,
EuiText,
EuiPanel,
EuiHorizontalRule,
} from '@elastic/eui';
interface Option {
label: string;
value: string;
checked: boolean;
defaultSortOrder?: string;
onClick: () => void;
}
interface Props {
sortOptions: Option[];
orderOptions: Option[];
sortField: string;
}
export const SortMenu = ({ sortOptions, orderOptions, sortField }: Props) => {
const [isPopoverOpen, setPopover] = useState(false);
const singleContextMenuPopoverId = useGeneratedHtmlId({
prefix: 'singleContextMenuPopover',
});
const onButtonClick = () => {
setPopover(!isPopoverOpen);
};
const closePopover = () => {
setPopover(false);
};
const button = (
<EuiButtonEmpty size="xs" iconType="arrowDown" iconSide="right" onClick={onButtonClick}>
{sortField}
</EuiButtonEmpty>
);
const items = [
<EuiPanel paddingSize="s" hasShadow={false}>
<EuiText size="xs">
<h4>{SORT_BY_TITLE}</h4>
</EuiText>
</EuiPanel>,
...sortOptions.map((option) => (
<ContextMenuItem option={option} onClosePopover={closePopover} />
)),
<EuiHorizontalRule key="hr" margin="none" />,
<EuiPanel paddingSize="s" hasShadow={false}>
<EuiText size="xs">
<h4>{ORDER_BY_TITLE}</h4>
</EuiText>
</EuiPanel>,
...orderOptions.map((option) => (
<ContextMenuItem option={option} onClosePopover={closePopover} />
)),
];
return (
<EuiPopover
id={singleContextMenuPopoverId}
button={button}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downLeft"
>
<EuiContextMenuPanel size="s" items={items} />
</EuiPopover>
);
};
const ContextMenuItem = ({
option,
onClosePopover,
}: {
option: Option;
onClosePopover: () => void;
}) => {
const getIconType = (checked: boolean) => {
return checked ? 'check' : 'empty';
};
return (
<EuiContextMenuItem
key={option.value}
icon={getIconType(option.checked)}
onClick={() => {
onClosePopover();
option.onClick();
}}
style={{
marginRight: 24,
}}
>
{option.label}
</EuiContextMenuItem>
);
};
const SORT_BY_TITLE = i18n.translate('xpack.synthetics.overview.sortPopover.sortBy.title', {
defaultMessage: 'Sort by',
});
const ORDER_BY_TITLE = i18n.translate('xpack.synthetics.overview.sortPopover.orderBy.title', {
defaultMessage: 'Order',
});

View file

@ -7,13 +7,13 @@
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { EuiLoadingElastic, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { useTrackPageview } from '@kbn/observability-plugin/public';
import { Redirect } from 'react-router-dom';
import { useEnablement } from '../../../hooks';
import { useSyntheticsRefreshContext } from '../../../contexts/synthetics_refresh_context';
import {
fetchMonitorOverviewAction,
quietFetchOverviewAction,
selectOverviewState,
selectServiceLocationsState,
} from '../../../state';
@ -34,7 +34,7 @@ export const OverviewPage: React.FC = () => {
const { refreshApp, lastRefresh } = useSyntheticsRefreshContext();
const { loading, pageState } = useSelector(selectOverviewState);
const { pageState } = useSelector(selectOverviewState);
const { loading: locationsLoading, locationsLoaded } = useSelector(selectServiceLocationsState);
useEffect(() => {
@ -48,10 +48,14 @@ export const OverviewPage: React.FC = () => {
if (!locationsLoading && !locationsLoaded) {
dispatch(getServiceLocations());
}
}, [dispatch, locationsLoaded, locationsLoading, pageState]);
}, [dispatch, locationsLoaded, locationsLoading]);
useEffect(() => {
dispatch(fetchMonitorOverviewAction.get(pageState));
}, [dispatch, pageState]);
useEffect(() => {
dispatch(quietFetchOverviewAction.get(pageState));
}, [dispatch, pageState, lastRefresh]);
const {
@ -69,14 +73,5 @@ export const OverviewPage: React.FC = () => {
return <Redirect to={MONITORS_ROUTE} />;
}
return !loading ? (
<OverviewGrid />
) : (
<EuiFlexGroup alignItems="center" justifyContent="center">
<EuiSpacer size="xxl" />
<EuiFlexItem grow={false}>
<EuiLoadingElastic size="xxl" />
</EuiFlexItem>
</EuiFlexGroup>
);
return <OverviewGrid />;
};

View file

@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { useLocationNames } from './use_location_names';
import { WrappedHelper } from '../utils/testing';
describe('useMonitorListFilters', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('returns map of id to name', () => {
const WrapperWithState = ({ children }: { children: React.ReactElement }) => {
return (
<WrappedHelper
state={{
serviceLocations: {
locationsLoaded: true,
loading: false,
locations: [
{
url: 'mockUrl',
id: 'us_central',
label: 'US Central',
isServiceManaged: true,
},
{
url: 'mockUrl',
id: 'us_east',
label: 'US East',
isServiceManaged: true,
},
],
},
}}
>
{children}
</WrappedHelper>
);
};
const { result } = renderHook(() => useLocationNames(), { wrapper: WrapperWithState });
expect(result.current).toEqual({
us_central: 'US Central',
us_east: 'US East',
});
});
});

View file

@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useMemo, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { selectServiceLocationsState, getServiceLocations } from '../state';
export function useLocationNames() {
const dispatch = useDispatch();
const { locationsLoaded, locations } = useSelector(selectServiceLocationsState);
useEffect(() => {
if (!locationsLoaded) {
dispatch(getServiceLocations());
}
});
return useMemo(
() =>
locations.reduce<Record<string, string>>((acc, location) => {
acc[location.id] = location.label;
return acc;
}, {}),
[locations]
);
}

View file

@ -0,0 +1,244 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import { useMonitorsSortedByStatus } from './use_monitors_sorted_by_status';
import { WrappedHelper } from '../utils/testing';
describe('useMonitorsSortedByStatus', () => {
const location1 = {
url: 'mockUrl',
id: 'us_central',
label: 'US Central',
isServiceManaged: true,
};
const location2 = {
url: 'mockUrl',
id: 'us_east',
label: 'US East',
isServiceManaged: true,
};
beforeEach(() => {
jest.clearAllMocks();
});
const WrapperWithState = ({
children,
sortOrder = 'asc',
}: {
children: React.ReactElement;
sortOrder: 'asc' | 'desc';
}) => {
return (
<WrappedHelper
state={{
serviceLocations: {
locationsLoaded: true,
loading: false,
locations: [location1, location2],
},
overview: {
pageState: {
perPage: 10,
sortOrder,
sortField: 'name.keyword',
},
status: {
upConfigs: [
{
configId: 'test-monitor-1',
heartbeatId: 'test-monitor-1',
location: location2.label,
},
{
configId: 'test-monitor-2',
heartbeatId: 'test-monitor-2',
location: location2.label,
},
{
configId: 'test-monitor-3',
heartbeatId: 'test-monitor-3',
location: location2.label,
},
],
downConfigs: [
{
configId: 'test-monitor-1',
heartbeatId: 'test-monitor-1',
location: location1.label,
},
{
configId: 'test-monitor-2',
heartbeatId: 'test-monitor-2',
location: location1.label,
},
{
configId: 'test-monitor-3',
heartbeatId: 'test-monitor-3',
location: location1.label,
},
],
},
data: {
total: 0,
allMonitorIds: [],
monitors: [
{
id: 'test-monitor-1',
name: 'Test monitor 1',
location: location1,
isEnabled: false,
},
{
id: 'test-monitor-1',
name: 'Test monitor 1',
location: location2,
isEnabled: true,
},
{
id: 'test-monitor-2',
name: 'Test monitor 2',
location: location1,
isEnabled: true,
},
{
id: 'test-monitor-2',
name: 'Test monitor 2',
location: location2,
isEnabled: true,
},
{
id: 'test-monitor-3',
name: 'Test monitor 3',
location: location1,
isEnabled: true,
},
{
id: 'test-monitor-3',
name: 'Test monitor 3',
location: location2,
isEnabled: true,
},
],
},
error: null,
loaded: false,
loading: false,
},
}}
>
{children}
</WrappedHelper>
);
};
it('returns monitors down first when sort order is asc', () => {
const { result } = renderHook(() => useMonitorsSortedByStatus(true), {
wrapper: WrapperWithState,
});
expect(result.current).toEqual({
monitorsSortedByStatus: [
{
id: 'test-monitor-2',
name: 'Test monitor 2',
location: location1,
isEnabled: true,
},
{
id: 'test-monitor-3',
name: 'Test monitor 3',
location: location1,
isEnabled: true,
},
{
id: 'test-monitor-1',
name: 'Test monitor 1',
location: location2,
isEnabled: true,
},
{
id: 'test-monitor-2',
name: 'Test monitor 2',
location: location2,
isEnabled: true,
},
{
id: 'test-monitor-3',
name: 'Test monitor 3',
location: location2,
isEnabled: true,
},
{
id: 'test-monitor-1',
name: 'Test monitor 1',
location: location1,
isEnabled: false,
},
],
downMonitors: {
'test-monitor-1': ['US Central'],
'test-monitor-2': ['US Central'],
'test-monitor-3': ['US Central'],
},
});
});
it('returns monitors up first when sort order is desc', () => {
const { result } = renderHook(() => useMonitorsSortedByStatus(true), {
wrapper: ({ children }: { children: React.ReactElement }) => (
<WrapperWithState sortOrder="desc">{children}</WrapperWithState>
),
});
expect(result.current).toEqual({
monitorsSortedByStatus: [
{
id: 'test-monitor-1',
name: 'Test monitor 1',
location: location2,
isEnabled: true,
},
{
id: 'test-monitor-2',
name: 'Test monitor 2',
location: location2,
isEnabled: true,
},
{
id: 'test-monitor-3',
name: 'Test monitor 3',
location: location2,
isEnabled: true,
},
{
id: 'test-monitor-2',
name: 'Test monitor 2',
location: location1,
isEnabled: true,
},
{
id: 'test-monitor-3',
name: 'Test monitor 3',
location: location1,
isEnabled: true,
},
{
id: 'test-monitor-1',
name: 'Test monitor 1',
location: location1,
isEnabled: false,
},
],
downMonitors: {
'test-monitor-1': ['US Central'],
'test-monitor-2': ['US Central'],
'test-monitor-3': ['US Central'],
},
});
});
});

View file

@ -0,0 +1,83 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useEffect, useMemo, useState, useRef } from 'react';
import { isEqual } from 'lodash';
import { useSelector } from 'react-redux';
import { MonitorOverviewItem } from '../../../../common/runtime_types';
import { selectOverviewState } from '../state/overview';
import { useLocationNames } from './use_location_names';
export function useMonitorsSortedByStatus(shouldUpdate: boolean) {
const {
pageState: { sortOrder },
data: { monitors },
status,
} = useSelector(selectOverviewState);
const [monitorsSortedByStatus, setMonitorsSortedByStatus] = useState<
Record<string, MonitorOverviewItem[]>
>({ up: [], down: [], disabled: [] });
const downMonitors = useRef<Record<string, string[]> | null>(null);
const currentMonitors = useRef<MonitorOverviewItem[] | null>(monitors);
const locationNames = useLocationNames();
useEffect(() => {
if (!status) {
return;
}
const { downConfigs } = status;
const downMonitorMap: Record<string, string[]> = {};
downConfigs.forEach(({ location, configId }) => {
if (downMonitorMap[configId]) {
downMonitorMap[configId].push(location);
} else {
downMonitorMap[configId] = [location];
}
});
if (
!isEqual(downMonitorMap, downMonitors.current) ||
!isEqual(monitors, currentMonitors.current)
) {
const orderedDownMonitors: MonitorOverviewItem[] = [];
const orderedUpMonitors: MonitorOverviewItem[] = [];
const orderedDisabledMonitors: MonitorOverviewItem[] = [];
monitors.forEach((monitor) => {
const monitorLocation = locationNames[monitor.location.id];
if (!monitor.isEnabled) {
orderedDisabledMonitors.push(monitor);
} else if (
Object.keys(downMonitorMap).includes(monitor.id) &&
downMonitorMap[monitor.id].includes(monitorLocation)
) {
orderedDownMonitors.push(monitor);
} else {
orderedUpMonitors.push(monitor);
}
});
downMonitors.current = downMonitorMap;
currentMonitors.current = monitors;
setMonitorsSortedByStatus({
down: orderedDownMonitors,
up: orderedUpMonitors,
disabled: orderedDisabledMonitors,
});
}
}, [monitors, locationNames, downMonitors, status]);
return useMemo(() => {
const upAndDownMonitors =
sortOrder === 'asc'
? [...monitorsSortedByStatus.down, ...monitorsSortedByStatus.up]
: [...monitorsSortedByStatus.up, ...monitorsSortedByStatus.down];
return {
monitorsSortedByStatus: [...upAndDownMonitors, ...monitorsSortedByStatus.disabled],
downMonitors: downMonitors.current,
};
}, [downMonitors, monitorsSortedByStatus, sortOrder]);
}

View file

@ -148,7 +148,7 @@ const getRoutes = (
values: { baseTitle },
}),
path: OVERVIEW_ROUTE,
component: () => <OverviewPage />,
component: OverviewPage,
dataTestSubj: 'syntheticsOverviewPage',
pageHeader: {
pageTitle: (

View file

@ -15,7 +15,9 @@ export const fetchMonitorOverviewAction = createAsyncAction<
MonitorOverviewResult
>('fetchMonitorOverviewAction');
export const setOverviewPerPageAction = createAction<number>('setOverviewPerPageAction');
export const setOverviewPageStateAction = createAction<Partial<MonitorOverviewPageState>>(
'setOverviewPageStateAction'
);
export const quietFetchOverviewAction = createAsyncAction<
MonitorOverviewPageState,

View file

@ -21,7 +21,7 @@ export const fetchMonitorOverview = async (
): Promise<MonitorOverviewResult> => {
return await apiService.get(
SYNTHETICS_API_URLS.SYNTHETICS_OVERVIEW,
{ perPage: pageState.perPage },
{ perPage: pageState.perPage, sortOrder: pageState.sortOrder, sortField: pageState.sortField },
MonitorOverviewResultCodec
);
};

View file

@ -17,7 +17,7 @@ import { fetchMonitorOverview, fetchOverviewStatus } from './api';
export function* fetchMonitorOverviewEffect() {
yield takeLeading(
fetchMonitorOverviewAction.get,
[fetchMonitorOverviewAction.get, quietFetchOverviewAction.get],
fetchEffectFactory(
fetchMonitorOverview,
fetchMonitorOverviewAction.success,
@ -26,17 +26,6 @@ export function* fetchMonitorOverviewEffect() {
);
}
export function* quietFetchOverviewEffect() {
yield takeLeading(
quietFetchOverviewAction.get,
fetchEffectFactory(
fetchMonitorOverview,
quietFetchOverviewAction.success,
quietFetchOverviewAction.fail
)
);
}
export function* fetchOverviewStatusEffect() {
yield takeLatest(
[fetchOverviewStatusAction.get, fetchUpsertSuccessAction],

View file

@ -17,7 +17,7 @@ import {
fetchMonitorOverviewAction,
fetchOverviewStatusAction,
quietFetchOverviewAction,
setOverviewPerPageAction,
setOverviewPageStateAction,
} from './actions';
export interface MonitorOverviewState {
@ -34,10 +34,12 @@ const initialState: MonitorOverviewState = {
data: {
total: 0,
allMonitorIds: [],
pages: {},
monitors: [],
},
pageState: {
perPage: 20,
perPage: 16,
sortOrder: 'asc',
sortField: 'status',
},
loading: false,
loaded: false,
@ -68,10 +70,10 @@ export const monitorOverviewReducer = createReducer(initialState, (builder) => {
.addCase(quietFetchOverviewAction.fail, (state, action) => {
state.error = action.payload;
})
.addCase(setOverviewPerPageAction, (state, action) => {
.addCase(setOverviewPageStateAction, (state, action) => {
state.pageState = {
...state.pageState,
perPage: action.payload,
...action.payload,
};
state.loaded = false;
})

View file

@ -7,4 +7,6 @@
export interface MonitorOverviewPageState {
perPage: number;
sortOrder: 'asc' | 'desc';
sortField: string;
}

View file

@ -11,11 +11,7 @@ import { fetchSyntheticsMonitorEffect } from './monitor_details';
import { fetchIndexStatusEffect } from './index_status';
import { fetchSyntheticsEnablementEffect } from './synthetics_enablement';
import { fetchMonitorListEffect, upsertMonitorEffect } from './monitor_list';
import {
fetchMonitorOverviewEffect,
quietFetchOverviewEffect,
fetchOverviewStatusEffect,
} from './overview';
import { fetchMonitorOverviewEffect, fetchOverviewStatusEffect } from './overview';
import { fetchServiceLocationsEffect } from './service_locations';
import { browserJourneyEffects } from './browser_journey';
@ -28,7 +24,6 @@ export const rootEffect = function* root(): Generator {
fork(fetchMonitorListEffect),
fork(fetchSyntheticsMonitorEffect),
fork(fetchMonitorOverviewEffect),
fork(quietFetchOverviewEffect),
fork(browserJourneyEffects),
fork(fetchOverviewStatusEffect),
fork(fetchNetworkEventsEffect),

View file

@ -87,11 +87,13 @@ export const mockState: SyntheticsAppState = {
overview: {
pageState: {
perPage: 10,
sortOrder: 'asc',
sortField: 'name.keyword',
},
data: {
total: 0,
allMonitorIds: [],
pages: {},
monitors: [],
},
error: null,
loaded: false,

View file

@ -6,7 +6,7 @@
*/
import { schema } from '@kbn/config-schema';
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
import { ConfigKey } from '../../../common/runtime_types';
import { ConfigKey, MonitorOverviewItem, SyntheticsMonitor } from '../../../common/runtime_types';
import { UMServerLibs } from '../../legacy_uptime/lib/lib';
import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes/types';
import { API_URLS, SYNTHETICS_API_URLS } from '../../../common/constants';
@ -112,55 +112,42 @@ export const getSyntheticsMonitorOverviewRoute: SyntheticsRestApiRouteFactory =
query: querySchema,
},
handler: async ({ request, savedObjectsClient, syntheticsMonitorClient }): Promise<any> => {
const { perPage = 5 } = request.query;
const { saved_objects: monitors } = await getMonitors(
{
perPage: 1000,
sortField: 'name.keyword',
sortOrder: 'asc',
page: 1,
},
syntheticsMonitorClient.syntheticsService,
savedObjectsClient
);
const allMonitorIds: string[] = [];
const pages: Record<number, unknown[]> = {};
let currentPage = 0;
let currentItem = 0;
let total = 0;
monitors.forEach((monitor) => {
/* collect all monitor ids for use
* in filtering overview requests */
const id = monitor.id;
allMonitorIds.push(id);
/* for reach location, add a config item */
const locations = monitor.attributes[ConfigKey.LOCATIONS];
locations.forEach((location) => {
const config = {
id,
name: monitor.attributes[ConfigKey.NAME],
location,
isEnabled: monitor.attributes[ConfigKey.ENABLED],
};
if (!pages[currentPage]) {
pages[currentPage] = [config];
} else {
pages[currentPage].push(config);
}
currentItem++;
total++;
if (currentItem % perPage === 0) {
currentPage++;
currentItem = 0;
}
});
const { sortField, sortOrder } = request.query;
const finder = savedObjectsClient.createPointInTimeFinder<SyntheticsMonitor>({
type: syntheticsMonitorType,
sortField: sortField === 'status' ? `${ConfigKey.NAME}.keyword` : sortField,
sortOrder,
perPage: 500,
});
const allMonitorIds: string[] = [];
let total = 0;
const allMonitors: MonitorOverviewItem[] = [];
for await (const result of finder.find()) {
/* collect all monitor ids for use
* in filtering overview requests */
result.saved_objects.forEach((monitor) => {
const id = monitor.id;
allMonitorIds.push(id);
/* for reach location, add a config item */
const locations = monitor.attributes[ConfigKey.LOCATIONS];
locations.forEach((location) => {
const config = {
id,
name: monitor.attributes[ConfigKey.NAME],
location,
isEnabled: monitor.attributes[ConfigKey.ENABLED],
};
allMonitors.push(config);
total++;
});
});
}
return {
pages,
monitors: allMonitors,
total,
allMonitorIds,
};

View file

@ -78,11 +78,18 @@ describe('current status route', () => {
'@timestamp': '2022-09-15T16:08:16.724Z',
monitor: {
status: 'up',
id: 'id1',
},
summary: {
up: 1,
down: 0,
},
config_id: 'id1',
observer: {
geo: {
name: 'test-location',
},
},
},
},
],
@ -106,11 +113,18 @@ describe('current status route', () => {
'@timestamp': '2022-09-15T16:09:16.724Z',
monitor: {
status: 'up',
id: 'id2',
},
summary: {
up: 1,
down: 0,
},
config_id: 'id2',
observer: {
geo: {
name: 'test-location',
},
},
},
},
],
@ -127,11 +141,18 @@ describe('current status route', () => {
'@timestamp': '2022-09-15T16:19:16.724Z',
monitor: {
status: 'down',
id: 'id2',
},
summary: {
down: 1,
up: 0,
},
config_id: 'id2',
observer: {
geo: {
name: 'test-location',
},
},
},
},
],
@ -146,6 +167,25 @@ describe('current status route', () => {
expect(await queryMonitorStatus(uptimeEsClient, 3, 140000, ['id1', 'id2'])).toEqual({
down: 1,
up: 2,
upConfigs: [
{
configId: 'id1',
heartbeatId: 'id1',
location: 'test-location',
},
{
configId: 'id2',
heartbeatId: 'id2',
location: 'test-location',
},
],
downConfigs: [
{
configId: 'id2',
heartbeatId: 'id2',
location: 'test-location',
},
],
});
});
@ -167,11 +207,18 @@ describe('current status route', () => {
'@timestamp': '2022-09-15T16:08:16.724Z',
monitor: {
status: 'up',
id: 'id1',
},
summary: {
up: 1,
down: 0,
},
config_id: 'id1',
observer: {
geo: {
name: 'test-location',
},
},
},
},
],
@ -195,11 +242,18 @@ describe('current status route', () => {
'@timestamp': '2022-09-15T16:09:16.724Z',
monitor: {
status: 'up',
id: 'id2',
},
summary: {
up: 1,
down: 0,
},
config_id: 'id2',
observer: {
geo: {
name: 'test-location',
},
},
},
},
],
@ -216,11 +270,18 @@ describe('current status route', () => {
'@timestamp': '2022-09-15T16:19:16.724Z',
monitor: {
status: 'down',
id: 'id2',
},
summary: {
up: 0,
down: 1,
},
config_id: 'id2',
observer: {
geo: {
name: 'test-location',
},
},
},
},
],
@ -242,6 +303,25 @@ describe('current status route', () => {
expect(await queryMonitorStatus(uptimeEsClient, 10000, 2500, ['id1', 'id2'])).toEqual({
down: 1,
up: 2,
upConfigs: [
{
configId: 'id1',
heartbeatId: 'id1',
location: 'test-location',
},
{
configId: 'id2',
heartbeatId: 'id2',
location: 'test-location',
},
],
downConfigs: [
{
configId: 'id2',
heartbeatId: 'id2',
location: 'test-location',
},
],
});
expect(esClient.search).toHaveBeenCalledTimes(2);
// These assertions are to ensure that we are paginating through the IDs we use for filtering

View file

@ -15,7 +15,7 @@ import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes';
import { getMonitors } from '../common';
import { UptimeEsClient } from '../../legacy_uptime/lib/lib';
import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client';
import { ConfigKey, OverviewStatus } from '../../../common/runtime_types';
import { ConfigKey, OverviewStatus, OverviewStatusMetaData } from '../../../common/runtime_types';
/**
* Helper function that converts a monitor's schedule to a value to use to generate
@ -36,7 +36,7 @@ export async function queryMonitorStatus(
maxLocations: number,
maxPeriod: number,
ids: Array<string | undefined>
): Promise<Pick<OverviewStatus, 'up' | 'down'>> {
): Promise<Omit<OverviewStatus, 'disabledCount'>> {
const idSize = Math.trunc(DEFAULT_MAX_ES_BUCKET_SIZE / maxLocations);
const pageCount = Math.ceil(ids.length / idSize);
const promises: Array<Promise<any>> = [];
@ -92,7 +92,7 @@ export async function queryMonitorStatus(
},
],
_source: {
includes: ['@timestamp', 'summary'],
includes: ['@timestamp', 'summary', 'monitor', 'observer', 'config_id'],
},
},
},
@ -107,20 +107,35 @@ export async function queryMonitorStatus(
}
let up = 0;
let down = 0;
const upConfigs: OverviewStatusMetaData[] = [];
const downConfigs: OverviewStatusMetaData[] = [];
for await (const response of promises) {
response.aggregations?.id.buckets.forEach(({ location }: { key: string; location: any }) => {
location.buckets.forEach(({ status }: { key: string; status: any }) => {
const downCount = status.hits.hits[0]._source.summary.down;
const upCount = status.hits.hits[0]._source.summary.up;
const configId = status.hits.hits[0]._source.config_id;
const heartbeatId = status.hits.hits[0]._source.monitor.id;
const locationName = status.hits.hits[0]._source.observer?.geo?.name;
if (upCount > 0) {
up += 1;
upConfigs.push({
configId,
heartbeatId,
location: locationName,
});
} else if (downCount > 0) {
down += 1;
downConfigs.push({
configId,
heartbeatId,
location: locationName,
});
}
});
});
}
return { up, down };
return { up, down, upConfigs, downConfigs };
}
/**
@ -169,7 +184,7 @@ export async function getStatus(
});
} while (monitors.saved_objects.length === monitors.per_page);
const { up, down } = await queryMonitorStatus(
const { up, down, upConfigs, downConfigs } = await queryMonitorStatus(
uptimeEsClient,
maxLocations,
maxPeriod,
@ -180,6 +195,8 @@ export async function getStatus(
disabledCount,
up,
down,
upConfigs,
downConfigs,
};
}

View file

@ -31216,7 +31216,6 @@
"xpack.synthetics.overview.heading": "Moniteurs",
"xpack.synthetics.overview.monitors.label": "Moniteurs",
"xpack.synthetics.overview.pageHeader.title": "Aperçu",
"xpack.synthetics.overview.pagination.ariaLabel": "Pagination pour laperçu du moniteur",
"xpack.synthetics.overviewPage.overviewCrumb": "Aperçu",
"xpack.synthetics.overviewPageLink.disabled.ariaLabel": "Bouton de pagination désactivé indiquant qu'aucune autre navigation ne peut être effectuée dans la liste des moniteurs.",
"xpack.synthetics.overviewPageLink.next.ariaLabel": "Page de résultats suivante",

View file

@ -31192,7 +31192,6 @@
"xpack.synthetics.overview.heading": "監視",
"xpack.synthetics.overview.monitors.label": "監視",
"xpack.synthetics.overview.pageHeader.title": "概要",
"xpack.synthetics.overview.pagination.ariaLabel": "監視概要のページネーション",
"xpack.synthetics.overviewPage.overviewCrumb": "概要",
"xpack.synthetics.overviewPageLink.disabled.ariaLabel": "無効になったページ付けボタンです。モニターリストがこれ以上ナビゲーションできないことを示しています。",
"xpack.synthetics.overviewPageLink.next.ariaLabel": "次の結果ページ",

View file

@ -31227,7 +31227,6 @@
"xpack.synthetics.overview.heading": "监测",
"xpack.synthetics.overview.monitors.label": "监测",
"xpack.synthetics.overview.pageHeader.title": "概览",
"xpack.synthetics.overview.pagination.ariaLabel": "监测概述的分页",
"xpack.synthetics.overviewPage.overviewCrumb": "概览",
"xpack.synthetics.overviewPageLink.disabled.ariaLabel": "禁用的分页按钮表示在监测列表中无法进行进一步导航。",
"xpack.synthetics.overviewPageLink.next.ariaLabel": "下页结果",

View file

@ -82,33 +82,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(apiResponse.body.allMonitorIds.sort()).eql(
savedMonitors.map((monitor) => monitor.id).sort()
);
expect(apiResponse.body.pages).to.have.keys(['0', '1']);
expect(apiResponse.body.pages[1].length).eql(20);
} finally {
await Promise.all(
savedMonitors.map((monitor) => {
return deleteMonitor(monitor.id);
})
);
}
});
it('adjusts pagination correctly', async () => {
let savedMonitors: SimpleSavedObject[] = [];
try {
const savedResponse = await Promise.all(monitors.map(saveMonitor));
savedMonitors = savedResponse;
const apiResponse = await supertest.get(
SYNTHETICS_API_URLS.SYNTHETICS_OVERVIEW + '?perPage=5'
);
expect(apiResponse.body.total).eql(monitors.length * 2);
expect(apiResponse.body.allMonitorIds.sort()).eql(
savedMonitors.map((monitor) => monitor.id).sort()
);
expect(apiResponse.body.pages).to.have.keys(['0', '1', '2', '3', '4']);
expect(apiResponse.body.pages[1].length).eql(5);
expect(apiResponse.body.monitors.length).eql(40);
} finally {
await Promise.all(
savedMonitors.map((monitor) => {

523
yarn.lock
View file

@ -1297,17 +1297,12 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@cspotcode/source-map-consumer@0.8.0":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==
"@cspotcode/source-map-support@0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5"
integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==
"@cspotcode/source-map-support@^0.8.0", "@cspotcode/source-map-support@^0.8.1":
version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
dependencies:
"@cspotcode/source-map-consumer" "0.8.0"
"@jridgewell/trace-mapping" "0.3.9"
"@csstools/selector-specificity@^2.0.1":
version "2.0.1"
@ -1658,24 +1653,31 @@
history "^4.9.0"
qs "^6.7.0"
"@elastic/synthetics@^1.0.0-beta.22":
version "1.0.0-beta.22"
resolved "https://registry.yarnpkg.com/@elastic/synthetics/-/synthetics-1.0.0-beta.22.tgz#1ec60e212e925ffaf7fd63619858ad6ce6dfa85d"
integrity sha512-hDFPqBuY30naAZct8UNRMhKNtsVbPV5nYApkwRf/7y3/SgQBXPnMkxUBlxy+2vkPR4QnKusvWROUxMMwgVnXyA==
"@elastic/synthetics@^1.0.0-beta.23":
version "1.0.0-beta.37"
resolved "https://registry.yarnpkg.com/@elastic/synthetics/-/synthetics-1.0.0-beta.37.tgz#ba89918107c7aa166bcf43bbd0cbe4ad6a3b4430"
integrity sha512-MSYu+n4SuZ31AbxWpha5WBS33Y/YTVEvOc8g3WkCIxSqa8iSAPgGEAOZimk/pHOzPZNXJcRKIpL01HnKvqd14g==
dependencies:
commander "^9.0.0"
"@cspotcode/source-map-support" "^0.8.1"
"@esbuild-plugins/node-resolve" "^0.1.4"
archiver "^5.3.1"
commander "^9.4.0"
deepmerge "^4.2.2"
expect "^27.0.2"
enquirer "^2.3.6"
esbuild "^0.14.54"
expect "^28.1.3"
http-proxy "^1.18.1"
kleur "^4.1.4"
micromatch "^4.0.4"
playwright-chromium "=1.14.0"
sharp "^0.30.1"
snakecase-keys "^3.2.1"
sonic-boom "^2.6.0"
source-map-support "^0.5.21"
ts-node "^10.5.0"
typescript "^4.5.5"
kleur "^4.1.5"
micromatch "^4.0.5"
playwright-chromium "=1.26.0"
playwright-core "=1.26.0"
sharp "^0.31.1"
snakecase-keys "^4.0.0"
sonic-boom "^3.2.0"
ts-node "^10.9.1"
typescript "^4.8.3"
undici "^5.10.0"
yaml "^2.1.1"
"@elastic/transport@^8.2.0":
version "8.2.0"
@ -1919,6 +1921,21 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
"@esbuild-plugins/node-resolve@^0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@esbuild-plugins/node-resolve/-/node-resolve-0.1.4.tgz#2257ef3b233c9cb3acd2ebde7d5a3d6874046d38"
integrity sha512-haFQ0qhxEpqtWWY0kx1Y5oE3sMyO1PcoSiWEPrAw6tm/ZOOLXjSs6Q+v1v9eyuVF0nNt50YEvrcrvENmyoMv5g==
dependencies:
"@types/resolve" "^1.17.1"
debug "^4.3.1"
escape-string-regexp "^4.0.0"
resolve "^1.19.0"
"@esbuild/linux-loong64@0.14.54":
version "0.14.54"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
"@eslint/eslintrc@^0.4.3":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
@ -2403,6 +2420,13 @@
dependencies:
jest-get-type "^28.0.2"
"@jest/expect-utils@^28.1.3":
version "28.1.3"
resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525"
integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==
dependencies:
jest-get-type "^28.0.2"
"@jest/fake-timers@^27.5.1":
version "27.5.1"
resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74"
@ -2462,6 +2486,13 @@
dependencies:
"@sinclair/typebox" "^0.23.3"
"@jest/schemas@^28.1.3":
version "28.1.3"
resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905"
integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==
dependencies:
"@sinclair/typebox" "^0.24.1"
"@jest/source-map@^27.5.1":
version "27.5.1"
resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf"
@ -2555,12 +2586,12 @@
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
"@jest/types@^28.1.1":
version "28.1.1"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.1.tgz#d059bbc80e6da6eda9f081f293299348bd78ee0b"
integrity sha512-vRXVqSg1VhDnB8bWcmvLzmg0Bt9CRKVgHPXqYwvWMX3TvAjeO+nRuK6+VdTKCtWOvYlmkF/HqNAL/z+N3B53Kw==
"@jest/types@^28.1.1", "@jest/types@^28.1.3":
version "28.1.3"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b"
integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==
dependencies:
"@jest/schemas" "^28.0.2"
"@jest/schemas" "^28.1.3"
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
"@types/node" "*"
@ -2599,6 +2630,14 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==
"@jridgewell/trace-mapping@0.3.9":
version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
dependencies:
"@jridgewell/resolve-uri" "^3.0.3"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.15"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
@ -4863,6 +4902,11 @@
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d"
integrity sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg==
"@sinclair/typebox@^0.24.1":
version "0.24.46"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.46.tgz#57501b58023776dbbae9e25619146286440be34c"
integrity sha512-ng4ut1z2MCBhK/NwDVwIQp3pAUOCs/KNaW3cBxdFB2xTDrOuo1xuNmpr/9HHFhxqIvHrs1NTH3KJg6q+JSy1Kw==
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
@ -7397,7 +7441,7 @@
resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.5.tgz#36d897708172ac2380cd486da7a3daf1161c1e23"
integrity sha512-8k/67Z95Goa6Lznuykxkfhq9YU3l1Qe6LNZmwde1u7802a3x8v44oq0j91DICclxatTr0rNnhXx7+VTIetSrSQ==
"@types/resolve@^1.20.1":
"@types/resolve@^1.17.1", "@types/resolve@^1.20.1":
version "1.20.1"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.1.tgz#3727e48042fda81e374f5d5cf2fa92288bf698f8"
integrity sha512-Ku5+GPFa12S3W26Uwtw+xyrtIpaZsGYHH6zxNbZlstmlvMYSZRzOwzwsXbxlVUbHyUucctSyuFtu6bNxwYomIw==
@ -9835,6 +9879,13 @@ builtin-status-codes@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
busboy@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893"
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
dependencies:
streamsearch "^1.1.0"
byte-size@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-8.1.0.tgz#6353d0bc14ab7a69abcefbf11f8db0145a862cb5"
@ -10655,20 +10706,15 @@ commander@^5.0.0, commander@^5.1.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
commander@^6.1.0, commander@^6.2.1:
commander@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
commander@^8.2.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
commander@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.0.0.tgz#86d58f24ee98126568936bd1d3574e0308a99a40"
integrity sha512-JJfP2saEKbQqvW+FI93OYUB4ByV5cizMpFMiiJI8xDbBvQvSkIk0VvQdn1CZ8mqAO8Loq2h0gYTYtDFUZUeERw==
commander@^9.4.0:
version "9.4.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd"
integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==
common-path-prefix@^3.0.0:
version "3.0.0"
@ -12514,6 +12560,14 @@ dot-case@^3.0.3:
no-case "^3.0.3"
tslib "^1.10.0"
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
dot-prop@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
@ -13078,6 +13132,133 @@ es6-weak-map@^2.0.2:
es6-iterator "^2.0.1"
es6-symbol "^3.1.1"
esbuild-android-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
esbuild-android-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
esbuild-darwin-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
esbuild-darwin-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
esbuild-freebsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
esbuild-freebsd-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
esbuild-linux-32@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
esbuild-linux-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
esbuild-linux-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
esbuild-linux-arm@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
esbuild-linux-mips64le@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
esbuild-linux-ppc64le@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
esbuild-linux-riscv64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
esbuild-linux-s390x@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
esbuild-netbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
esbuild-openbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
esbuild-sunos-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
esbuild-windows-32@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
esbuild-windows-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
esbuild-windows-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
esbuild@^0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
optionalDependencies:
"@esbuild/linux-loong64" "0.14.54"
esbuild-android-64 "0.14.54"
esbuild-android-arm64 "0.14.54"
esbuild-darwin-64 "0.14.54"
esbuild-darwin-arm64 "0.14.54"
esbuild-freebsd-64 "0.14.54"
esbuild-freebsd-arm64 "0.14.54"
esbuild-linux-32 "0.14.54"
esbuild-linux-64 "0.14.54"
esbuild-linux-arm "0.14.54"
esbuild-linux-arm64 "0.14.54"
esbuild-linux-mips64le "0.14.54"
esbuild-linux-ppc64le "0.14.54"
esbuild-linux-riscv64 "0.14.54"
esbuild-linux-s390x "0.14.54"
esbuild-netbsd-64 "0.14.54"
esbuild-openbsd-64 "0.14.54"
esbuild-sunos-64 "0.14.54"
esbuild-windows-32 "0.14.54"
esbuild-windows-64 "0.14.54"
esbuild-windows-arm64 "0.14.54"
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@ -13647,7 +13828,7 @@ expect@^26.6.2:
jest-message-util "^26.6.2"
jest-regex-util "^26.0.0"
expect@^27.0.2, expect@^27.5.1:
expect@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74"
integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==
@ -13668,6 +13849,17 @@ expect@^28.1.1:
jest-message-util "^28.1.1"
jest-util "^28.1.1"
expect@^28.1.3:
version "28.1.3"
resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec"
integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==
dependencies:
"@jest/expect-utils" "^28.1.3"
jest-get-type "^28.0.2"
jest-matcher-utils "^28.1.3"
jest-message-util "^28.1.3"
jest-util "^28.1.3"
expiry-js@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/expiry-js/-/expiry-js-0.1.7.tgz#76be8c05e572bf936df40c1766448d0b3b2f555f"
@ -16921,6 +17113,16 @@ jest-diff@^28.1.1:
jest-get-type "^28.0.2"
pretty-format "^28.1.1"
jest-diff@^28.1.3:
version "28.1.3"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f"
integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==
dependencies:
chalk "^4.0.0"
diff-sequences "^28.1.1"
jest-get-type "^28.0.2"
pretty-format "^28.1.3"
jest-docblock@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0"
@ -17091,6 +17293,16 @@ jest-matcher-utils@^28.1.1:
jest-get-type "^28.0.2"
pretty-format "^28.1.1"
jest-matcher-utils@^28.1.3:
version "28.1.3"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e"
integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==
dependencies:
chalk "^4.0.0"
jest-diff "^28.1.3"
jest-get-type "^28.0.2"
pretty-format "^28.1.3"
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"
@ -17121,18 +17333,18 @@ jest-message-util@^27.5.1:
slash "^3.0.0"
stack-utils "^2.0.3"
jest-message-util@^28.1.1:
version "28.1.1"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.1.tgz#60aa0b475cfc08c8a9363ed2fb9108514dd9ab89"
integrity sha512-xoDOOT66fLfmTRiqkoLIU7v42mal/SqwDKvfmfiWAdJMSJiU+ozgluO7KbvoAgiwIrrGZsV7viETjc8GNrA/IQ==
jest-message-util@^28.1.1, jest-message-util@^28.1.3:
version "28.1.3"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d"
integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==
dependencies:
"@babel/code-frame" "^7.12.13"
"@jest/types" "^28.1.1"
"@jest/types" "^28.1.3"
"@types/stack-utils" "^2.0.0"
chalk "^4.0.0"
graceful-fs "^4.2.9"
micromatch "^4.0.4"
pretty-format "^28.1.1"
pretty-format "^28.1.3"
slash "^3.0.0"
stack-utils "^2.0.3"
@ -17389,6 +17601,18 @@ jest-util@^28.1.1:
graceful-fs "^4.2.9"
picomatch "^2.2.3"
jest-util@^28.1.3:
version "28.1.3"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0"
integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==
dependencies:
"@jest/types" "^28.1.3"
"@types/node" "*"
chalk "^4.0.0"
ci-info "^3.2.0"
graceful-fs "^4.2.9"
picomatch "^2.2.3"
jest-validate@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067"
@ -17462,11 +17686,6 @@ jpeg-js@^0.3.2:
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.3.7.tgz#471a89d06011640592d314158608690172b1028d"
integrity sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==
jpeg-js@^0.4.2:
version "0.4.3"
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b"
integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==
jquery@^3.5.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
@ -17864,10 +18083,10 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
kleur@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
kleur@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780"
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
klona@^2.0.4:
version "2.0.4"
@ -18460,6 +18679,13 @@ lower-case@^2.0.1:
dependencies:
tslib "^1.10.0"
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
dependencies:
tslib "^2.0.3"
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
@ -19099,7 +19325,7 @@ mime@1.6.0, mime@^1.4.1:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
mime@^2.4.4, mime@^2.4.6:
mime@^2.4.4:
version "2.5.2"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
@ -19660,6 +19886,14 @@ no-case@^3.0.3:
lower-case "^2.0.1"
tslib "^1.10.0"
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
dependencies:
lower-case "^2.0.2"
tslib "^2.0.3"
nock@12.0.3:
version "12.0.3"
resolved "https://registry.yarnpkg.com/nock/-/nock-12.0.3.tgz#83f25076dbc4c9aa82b5cdf54c9604c7a778d1c9"
@ -20979,54 +21213,29 @@ platform@^1.3.0:
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444"
integrity sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==
playwright-chromium@=1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/playwright-chromium/-/playwright-chromium-1.14.0.tgz#b153eb96412fd6a4fa8d9233a4fdf694fc9f3139"
integrity sha512-qWQN9VTPhvEZdRpn1564EOtiNU+hRHhogKg1heBX9VsfGy6WHytR9XPFJjD4M6fhNAV1WKM2McVPYIbi1EOYww==
playwright-chromium@=1.26.0:
version "1.26.0"
resolved "https://registry.yarnpkg.com/playwright-chromium/-/playwright-chromium-1.26.0.tgz#fa4e75a2034a016b9e2e825fc6577f3efb0d2792"
integrity sha512-4hDiVmMKmtuHW5ne11S1HCQTdL+wytprQMhWYecEjMSIKBR1DJ3JLrcUDgqA0L5Jzi/CBKYQQk6TOVlTjXybXQ==
dependencies:
commander "^6.1.0"
debug "^4.1.1"
extract-zip "^2.0.1"
https-proxy-agent "^5.0.0"
jpeg-js "^0.4.2"
mime "^2.4.6"
pngjs "^5.0.0"
progress "^2.0.3"
proper-lockfile "^4.1.1"
proxy-from-env "^1.1.0"
rimraf "^3.0.2"
stack-utils "^2.0.3"
ws "^7.4.6"
yazl "^2.5.1"
playwright-core "1.26.0"
playwright-core@=1.17.1:
version "1.17.1"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.17.1.tgz#a16e0f89284a0ed8ae6d77e1c905c84b8a2ba022"
integrity sha512-C3c8RpPiC3qr15fRDN6dx6WnUkPLFmST37gms2aoHPDRvp7EaGDPMMZPpqIm/QWB5J40xDrQCD4YYHz2nBTojQ==
dependencies:
commander "^8.2.0"
debug "^4.1.1"
extract-zip "^2.0.1"
https-proxy-agent "^5.0.0"
jpeg-js "^0.4.2"
mime "^2.4.6"
pngjs "^5.0.0"
progress "^2.0.3"
proper-lockfile "^4.1.1"
proxy-from-env "^1.1.0"
rimraf "^3.0.2"
socks-proxy-agent "^6.1.0"
stack-utils "^2.0.3"
ws "^7.4.6"
yauzl "^2.10.0"
yazl "^2.5.1"
playwright-core@1.26.0, playwright-core@=1.26.0:
version "1.26.0"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.26.0.tgz#850228f0638d410a5cdd69800d552f60e4d295cd"
integrity sha512-p8huU8eU4gD3VkJd3DA1nA7R3XA6rFvFL+1RYS96cSljCF2yJE9CWEHTPF4LqX8KN9MoWCrAfVKP5381X3CZqg==
playwright@^1.17.1:
version "1.17.1"
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.17.1.tgz#a6d63302ee40f41283c4bf869de261c4743a787c"
integrity sha512-DisCkW9MblDJNS3rG61p8LiLA2WA7IY/4A4W7DX4BphWe/HuWjKmGQptuk4NVIh5UuSwXpW/jaH2+ZgjHs3GMA==
playwright-core@1.27.1:
version "1.27.1"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.27.1.tgz#840ef662e55a3ed759d8b5d3d00a5f885a7184f4"
integrity sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q==
playwright@^1.26.0:
version "1.27.1"
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.27.1.tgz#4eecac5899566c589d4220ca8acc16abe8a67450"
integrity sha512-xXYZ7m36yTtC+oFgqH0eTgullGztKSRMb4yuwLPl8IYSmgBM88QiB+3IWb1mRIC9/NNwcgbG0RwtFlg+EAFQHQ==
dependencies:
playwright-core "=1.17.1"
playwright-core "1.27.1"
plugin-error@^1.0.1:
version "1.0.1"
@ -21065,11 +21274,6 @@ pngjs@^3.3.3, pngjs@^3.4.0:
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==
pngjs@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
pngjs@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821"
@ -21521,6 +21725,16 @@ pretty-format@^28.1.1:
ansi-styles "^5.0.0"
react-is "^18.0.0"
pretty-format@^28.1.3:
version "28.1.3"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5"
integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==
dependencies:
"@jest/schemas" "^28.1.3"
ansi-regex "^5.0.1"
ansi-styles "^5.0.0"
react-is "^18.0.0"
pretty-format@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385"
@ -21572,7 +21786,7 @@ process@^0.11.10:
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
progress@2.0.3, progress@^2.0.0, progress@^2.0.3:
progress@2.0.3, progress@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
@ -21657,15 +21871,6 @@ propagate@^2.0.0:
resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45"
integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==
proper-lockfile@^4.1.1:
version "4.1.2"
resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f"
integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==
dependencies:
graceful-fs "^4.2.4"
retry "^0.12.0"
signal-exit "^3.0.2"
property-information@^5.0.0, property-information@^5.0.1, property-information@^5.3.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.5.0.tgz#4dc075d493061a82e2b7d096f406e076ed859943"
@ -23990,10 +24195,10 @@ shallowequal@^1.1.0:
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
sharp@^0.30.1:
version "0.30.7"
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.30.7.tgz#7862bda98804fdd1f0d5659c85e3324b90d94c7c"
integrity sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==
sharp@^0.31.1:
version "0.31.1"
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.31.1.tgz#b2f7076d381a120761aa93700cadefcf90a22458"
integrity sha512-GR8M1wBwOiFKLkm9JPun27OQnNRZdHfSf9VwcdZX6UrRmM1/XnOrLFTF0GAil+y/YK4E6qcM/ugxs80QirsHxg==
dependencies:
color "^4.2.3"
detect-libc "^2.0.1"
@ -24147,13 +24352,21 @@ smart-buffer@^4.1.0:
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
snakecase-keys@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/snakecase-keys/-/snakecase-keys-3.2.1.tgz#ce5d1a2de8a93c939d7992f76f2743aa59f3d5ad"
integrity sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
snakecase-keys@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/snakecase-keys/-/snakecase-keys-4.0.2.tgz#72e28112b77753a68a4eeb110efec05ab391e190"
integrity sha512-ZFCo3zZtNN43cy2j4fQDHPxS557Uuzn887FBmDdaSB41D8l/MayuvaSrIlCXGFhZ8sXwrHiNaZiIPpKzi88gog==
dependencies:
map-obj "^4.1.0"
to-snake-case "^1.0.0"
snake-case "^3.0.4"
snap-shot-compare@2.8.3:
version "2.8.3"
@ -24218,7 +24431,7 @@ sockjs@^0.3.24:
uuid "^8.3.2"
websocket-driver "^0.7.4"
socks-proxy-agent@^6.0.0, socks-proxy-agent@^6.1.0:
socks-proxy-agent@^6.0.0:
version "6.1.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87"
integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==
@ -24243,10 +24456,10 @@ sonic-boom@^1.0.2:
atomic-sleep "^1.0.0"
flatstr "^1.0.12"
sonic-boom@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.6.0.tgz#8786fc78be07c18a90381acd816d1d4afe3537a2"
integrity sha512-6xYZFRmDEtxGqfOKcDQ4cPLrNa0SPEDI+wlzDAHowXE6YV42NeXqg9mP2KkiM8JVu3lHfZ2iQKYlGOz+kTpphg==
sonic-boom@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.2.0.tgz#ce9f2de7557e68be2e52c8df6d9b052e7d348143"
integrity sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA==
dependencies:
atomic-sleep "^1.0.0"
@ -24296,7 +24509,7 @@ source-map-support@0.5.9:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.20, source-map-support@^0.5.21, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.20:
source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.20, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.20:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
@ -24685,6 +24898,11 @@ stream-to-async-iterator@^0.2.0:
resolved "https://registry.yarnpkg.com/stream-to-async-iterator/-/stream-to-async-iterator-0.2.0.tgz#bef5c885e9524f98b2fa5effecc357bd58483780"
integrity sha1-vvXIhelST5iy+l7/7MNXvVhIN4A=
streamsearch@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
strict-uri-encode@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
@ -25608,13 +25826,6 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
to-snake-case@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/to-snake-case/-/to-snake-case-1.0.0.tgz#ce746913897946019a87e62edfaeaea4c608ab8c"
integrity sha1-znRpE4l5RgGah+Yu366upMYIq4w=
dependencies:
to-space-case "^1.0.0"
to-source-code@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/to-source-code/-/to-source-code-1.0.2.tgz#dd136bdb1e1dbd80bbeacf088992678e9070bfea"
@ -25788,12 +25999,12 @@ ts-morph@^13.0.2:
"@ts-morph/common" "~0.12.2"
code-block-writer "^11.0.0"
ts-node@^10.5.0:
version "10.7.0"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5"
integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
ts-node@^10.9.1:
version "10.9.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
dependencies:
"@cspotcode/source-map-support" "0.7.0"
"@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
"@tsconfig/node12" "^1.0.7"
"@tsconfig/node14" "^1.0.0"
@ -25804,7 +26015,7 @@ ts-node@^10.5.0:
create-require "^1.1.0"
diff "^4.0.1"
make-error "^1.1.1"
v8-compile-cache-lib "^3.0.0"
v8-compile-cache-lib "^3.0.1"
yn "3.1.1"
ts-pnp@^1.1.6:
@ -25976,7 +26187,7 @@ typescript-tuple@^2.2.1:
dependencies:
typescript-compare "^0.0.2"
typescript@4.6.3, typescript@^3.3.3333, typescript@^3.5.3, typescript@^4.5.5:
typescript@4.6.3, typescript@^3.3.3333, typescript@^3.5.3, typescript@^4.8.3:
version "4.6.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==
@ -26029,6 +26240,13 @@ undici@^5.1.1:
resolved "https://registry.yarnpkg.com/undici/-/undici-5.8.2.tgz#071fc8a6a5d24db0ad510ad442f607d9b09d5eec"
integrity sha512-3KLq3pXMS0Y4IELV045fTxqz04Nk9Ms7yfBBHum3yxsTR4XNn+ZCaUbf/mWitgYDAhsplQ0B1G4S5D345lMO3A==
undici@^5.10.0:
version "5.11.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.11.0.tgz#1db25f285821828fc09d3804b9e2e934ae86fc13"
integrity sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==
dependencies:
busboy "^1.6.0"
unfetch@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
@ -26519,10 +26737,10 @@ uuid@^8.3.0, uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
v8-compile-cache-lib@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8"
integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0:
version "2.3.0"
@ -27764,6 +27982,11 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.3.tgz#9b3a4c8aff9821b696275c79a8bee8399d945207"
integrity sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==
yargs-parser@20.2.4, yargs-parser@^20.2.2, yargs-parser@^20.2.3:
version "20.2.4"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"