From 38a4211fea7908ba25056a9585fb5bf04d1bf2b6 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Tue, 15 Mar 2022 12:36:02 +0100 Subject: [PATCH 01/87] chore(helm): nginx service dedicated name block Signed-off-by: rjshrjndrn --- .../openreplay/charts/nginx-ingress/templates/service.yaml | 2 +- scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/service.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/service.yaml index 6c978f7f6..f20d4fc38 100644 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/service.yaml +++ b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/service.yaml @@ -19,7 +19,7 @@ spec: - port: {{ .port }} targetPort: {{ .targetPort }} protocol: TCP - name: {{ .targetPort }} + name: {{ .name }} {{- end }} selector: {{- include "nginx-ingress.selectorLabels" . | nindent 4 }} diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml index 6984c1938..1f7169c0d 100644 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml +++ b/scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml @@ -42,8 +42,10 @@ service: ports: - port: 80 targetPort: http + name: http - port: 443 targetPort: https + name: https ingress: enabled: false From 38b536aae152e1464009fd56dbf916efce8e5434 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 18 Mar 2022 13:31:02 +0100 Subject: [PATCH 02/87] feat(ui) - dashboards wip --- frontend/app/Router.js | 8 +- frontend/app/api_client.js | 1 - .../app/components/Dashboard/NewDashboard.tsx | 45 ++++ .../Dashboard/WidgetView/WidgetView.tsx | 11 + .../components/Dashboard/WidgetView/index.ts | 1 + .../Dashboard/WidgetWrapper/WidgetWrapper.tsx | 34 +++ .../Dashboard/WidgetWrapper/index.ts | 1 + .../DashboardView/DashboardView.tsx | 20 ++ .../components/DashboardView/index.ts | 1 + .../components/Dashboard/store/dashboard.ts | 115 +++++++++ .../Dashboard/store/dashboardStore.ts | 233 ++++++++++++++++++ .../app/components/Dashboard/store/index.ts | 1 + .../app/components/Dashboard/store/store.tsx | 15 ++ .../app/components/Dashboard/store/widget.ts | 61 +++++ frontend/app/components/Header/Header.js | 2 +- frontend/app/initialize.js | 8 +- frontend/app/routes.js | 4 +- 17 files changed, 555 insertions(+), 6 deletions(-) create mode 100644 frontend/app/components/Dashboard/NewDashboard.tsx create mode 100644 frontend/app/components/Dashboard/WidgetView/WidgetView.tsx create mode 100644 frontend/app/components/Dashboard/WidgetView/index.ts create mode 100644 frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx create mode 100644 frontend/app/components/Dashboard/WidgetWrapper/index.ts create mode 100644 frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardView/index.ts create mode 100644 frontend/app/components/Dashboard/store/dashboard.ts create mode 100644 frontend/app/components/Dashboard/store/dashboardStore.ts create mode 100644 frontend/app/components/Dashboard/store/index.ts create mode 100644 frontend/app/components/Dashboard/store/store.tsx create mode 100644 frontend/app/components/Dashboard/store/widget.ts diff --git a/frontend/app/Router.js b/frontend/app/Router.js index 89fbdd343..665ff4df6 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -14,7 +14,8 @@ import SessionPure from 'Components/Session/Session'; import LiveSessionPure from 'Components/Session/LiveSession'; import AssistPure from 'Components/Assist'; import BugFinderPure from 'Components/BugFinder/BugFinder'; -import DashboardPure from 'Components/Dashboard/Dashboard'; +import DashboardPure from 'Components/Dashboard/NewDashboard'; +import WidgetViewPure from 'Components/Dashboard/WidgetView'; import ErrorsPure from 'Components/Errors/Errors'; import Header from 'Components/Header/Header'; // import ResultsModal from 'Shared/Results/ResultsModal'; @@ -35,6 +36,7 @@ import { setSessionPath } from 'Duck/sessions'; const BugFinder = withSiteIdUpdater(BugFinderPure); const Dashboard = withSiteIdUpdater(DashboardPure); +const WidgetView = withSiteIdUpdater(WidgetViewPure); const Session = withSiteIdUpdater(SessionPure); const LiveSession = withSiteIdUpdater(LiveSessionPure); const Assist = withSiteIdUpdater(AssistPure); @@ -46,7 +48,8 @@ const FunnelIssue = withSiteIdUpdater(FunnelIssueDetails); const withSiteId = routes.withSiteId; const withObTab = routes.withObTab; -const DASHBOARD_PATH = routes.dashboard(); +const DASHBOARD_PATH = routes.dashboardSelected(); +const WIDGET_PATAH = routes.dashboardMetric(); const SESSIONS_PATH = routes.sessions(); const ASSIST_PATH = routes.assist(); const ERRORS_PATH = routes.errors(); @@ -180,6 +183,7 @@ class Router extends React.Component { } + diff --git a/frontend/app/api_client.js b/frontend/app/api_client.js index a42f19468..7a725dd31 100644 --- a/frontend/app/api_client.js +++ b/frontend/app/api_client.js @@ -1,5 +1,4 @@ import store from 'App/store'; - import { queried } from './routes'; const siteIdRequiredPaths = [ diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx new file mode 100644 index 000000000..252e8bc53 --- /dev/null +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -0,0 +1,45 @@ +import React, { useEffect } from 'react'; +import { Switch, Route, Redirect } from 'react-router'; +import withPageTitle from 'HOCs/withPageTitle'; +import { observer } from "mobx-react-lite"; +import { withDashboardStore } from './store/store'; +import { withRouter } from 'react-router-dom'; +import DashboardView from './components/DashboardView'; +import { dashboardSelected, dashboardMetric, withSiteId } from 'App/routes'; + +function NewDashboard(props) { + const { store, match: { params: { siteId, dashboardId, metricId } } } = props; + const dashboard = store.selectedDashboard; + + useEffect(() => { + store.setSiteId(siteId); + if (dashboardId) { + store.selectDashboardById(dashboardId); + } else { + store.selectDefaultDashboard(); + } + }, [dashboardId]); + + return ( +
+
+ MENU +
+
+ + + + + +

Metric

+
+ +
+
+
+ ); +} + +export default withPageTitle('New Dashboard')( + withRouter(withDashboardStore(observer(NewDashboard))) +); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx new file mode 100644 index 000000000..6b2e10805 --- /dev/null +++ b/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +function WidgetView(props) { + return ( +
+ Widget view +
+ ); +} + +export default WidgetView; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/WidgetView/index.ts b/frontend/app/components/Dashboard/WidgetView/index.ts new file mode 100644 index 000000000..7bafa7a72 --- /dev/null +++ b/frontend/app/components/Dashboard/WidgetView/index.ts @@ -0,0 +1 @@ +export { default } from './WidgetView' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx new file mode 100644 index 000000000..35e088d52 --- /dev/null +++ b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { observer } from "mobx-react-lite"; +import { useDashboardStore } from '../store/store'; +import cn from 'classnames'; +import { Link } from 'UI'; +import { dashboardMetric, withSiteId } from 'App/routes'; + +function WidgetWrapper(props) { + const { widget } = props; + const store: any = useDashboardStore(); + const dashboard = store.selectedDashboard; + const siteId = store.siteId; + + return ( +
+ +
+ {widget.name} - {widget.position} +
+ +
+
+ +
+ +
+ +
+ ); +} + +export default observer(WidgetWrapper); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/WidgetWrapper/index.ts b/frontend/app/components/Dashboard/WidgetWrapper/index.ts new file mode 100644 index 000000000..83890df93 --- /dev/null +++ b/frontend/app/components/Dashboard/WidgetWrapper/index.ts @@ -0,0 +1 @@ +export { default } from './WidgetWrapper'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx new file mode 100644 index 000000000..850726789 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import WidgetWrapper from '../../WidgetWrapper'; +import { observer } from 'mobx-react-lite'; +import { withDashboardStore } from '../../store/store'; + +function DashboardView(props) { + const { store } = props; + const dashboard = store.selectedDashboard + const list = dashboard?.widgets; + return dashboard ? ( +
+ test {dashboard.dashboardId} +
+ {list && list.map(item => )} +
+
+ ) :

Loading...

; +} + +export default withDashboardStore(observer(DashboardView)); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/index.ts b/frontend/app/components/Dashboard/components/DashboardView/index.ts new file mode 100644 index 000000000..569832baa --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardView/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardView' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/dashboard.ts b/frontend/app/components/Dashboard/store/dashboard.ts new file mode 100644 index 000000000..9395b7c1b --- /dev/null +++ b/frontend/app/components/Dashboard/store/dashboard.ts @@ -0,0 +1,115 @@ +import { makeAutoObservable, makeObservable, observable, action, runInAction, computed, reaction } from "mobx" +import Widget from "./widget" +// import APIClient from 'App/api_client'; + +export default class Dashboard { + dashboardId: any = undefined + name: string = "New Dashboard" + isPriavte: boolean = false + widgets: Widget[] = [] + isValid: boolean = false + isPinned: boolean = false + + constructor() { + makeAutoObservable(this, { + name: observable, + isPriavte: observable, + widgets: observable, + isValid: observable, + + toJson: action, + fromJson: action, + addWidget: action, + removeWidget: action, + updateWidget: action, + getWidget: action, + getWidgetIndex: action, + getWidgetByIndex: action, + getWidgetCount: action, + getWidgetIndexByWidgetId: action, + validate: action, + sortWidgets: action, + swapWidgetPosition: action, + }) + } + + toJson() { + return { + dashboardId: this.dashboardId, + name: this.name, + isPrivate: this.isPriavte, + widgets: this.widgets.map(w => w.toJson()) + } + } + + fromJson(json: any) { + runInAction(() => { + this.dashboardId = json.dashboardId + this.name = json.name + this.isPriavte = json.isPrivate + this.widgets = json.widgets.map(w => new Widget().fromJson(w)) + }) + return this + } + + validate() { + this.isValid = this.name.length > 0 + } + + addWidget(widget: Widget) { + this.widgets.push(widget) + } + + removeWidget(widgetId: string) { + this.widgets = this.widgets.filter(w => w.widgetId !== widgetId) + } + + updateWidget(widget: Widget) { + const index = this.widgets.findIndex(w => w.widgetId === widget.widgetId) + if (index >= 0) { + this.widgets[index] = widget + } + } + + getWidget(widgetId: string) { + return this.widgets.find(w => w.widgetId === widgetId) + } + + getWidgetIndex(widgetId: string) { + return this.widgets.findIndex(w => w.widgetId === widgetId) + } + + getWidgetByIndex(index: number) { + return this.widgets[index] + } + + getWidgetCount() { + return this.widgets.length + } + + getWidgetIndexByWidgetId(widgetId: string) { + return this.widgets.findIndex(w => w.widgetId === widgetId) + } + + swapWidgetPosition(positionA, positionB) { + const widgetA = this.widgets[positionA] + const widgetB = this.widgets[positionB] + this.widgets[positionA] = widgetB + this.widgets[positionB] = widgetA + + widgetA.position = positionB + widgetB.position = positionA + } + + sortWidgets() { + this.widgets = this.widgets.sort((a, b) => { + if (a.position > b.position) { + return 1 + } else if (a.position < b.position) { + return -1 + } else { + return 0 + } + }) + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts new file mode 100644 index 000000000..1227b5611 --- /dev/null +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -0,0 +1,233 @@ +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import Dashboard from "./dashboard" +import APIClient from 'App/api_client'; +import Widget from "./widget"; + +export default class DashboardStore { + dashboards: Dashboard[] = [] + selectedDashboard: Dashboard | null = null + isLoading: boolean = false + siteId: any = null + + private client = new APIClient() + + constructor() { + makeAutoObservable(this, { + dashboards: observable, + selectedDashboard: observable, + isLoading: observable, + + addDashboard: action, + removeDashboard: action, + updateDashboard: action, + getDashboard: action, + getDashboardIndex: action, + getDashboardByIndex: action, + getDashboardCount: action, + getDashboardIndexByDashboardId: action, + selectDashboardById: action, + toJson: action, + fromJson: action, + setSiteId: action, + }) + + + // TODO remove this sample data + this.dashboards = sampleDashboards + this.selectedDashboard = sampleDashboards[0] + + // setInterval(() => { + // this.selectedDashboard?.addWidget(getRandomWidget()) + // }, 3000) + + // setInterval(() => { + // this.selectedDashboard?.widgets[4].update({ position: 2 }) + // this.selectedDashboard?.swapWidgetPosition(2, 0) + // }, 3000) + } + + fetchList() { + this.isLoading = true + + this.client.get('/dashboards') + .then(response => { + runInAction(() => { + this.dashboards = response.data.map(d => new Dashboard().fromJson(d)) + this.isLoading = false + }) + } + ) + } + + fetch(dashboardId: string) { + this.isLoading = true + this.client.get(`/dashboards/${dashboardId}`) + .then(response => { + runInAction(() => { + this.selectedDashboard = new Dashboard().fromJson(response.data) + this.isLoading = false + }) + } + ) + } + + save(dashboard: Dashboard) { + dashboard.validate() + if (dashboard.isValid) { + this.isLoading = true + if (dashboard.dashboardId) { + this.client.put(`/dashboards/${dashboard.dashboardId}`, dashboard.toJson()) + .then(response => { + runInAction(() => { + this.isLoading = false + }) + } + ) + } else { + this.client.post('/dashboards', dashboard.toJson()) + .then(response => { + runInAction(() => { + this.isLoading = false + }) + } + ) + } + } else { + alert("Invalid dashboard") // TODO show validation errors + } + } + + saveDashboardWidget(dashboard: Dashboard, widget: Widget) { + widget.validate() + if (widget.isValid) { + this.isLoading = true + if (widget.widgetId) { + this.client.put(`/dashboards/${dashboard.dashboardId}/widgets/${widget.widgetId}`, widget.toJson()) + .then(response => { + runInAction(() => { + this.isLoading = false + }) + } + ) + } else { + this.client.post(`/dashboards/${dashboard.dashboardId}/widgets`, widget.toJson()) + .then(response => { + runInAction(() => { + this.isLoading = false + }) + } + ) + } + } + } + + delete(dashboard: Dashboard) { + this.isLoading = true + this.client.delete(`/dashboards/${dashboard.dashboardId}`) + .then(response => { + runInAction(() => { + this.isLoading = false + }) + } + ) + } + + toJson() { + return { + dashboards: this.dashboards.map(d => d.toJson()) + } + } + + fromJson(json: any) { + runInAction(() => { + this.dashboards = json.dashboards.map(d => new Dashboard().fromJson(d)) + }) + return this + } + + initDashboard(dashboard: Dashboard | null) { + this.selectedDashboard = dashboard || new Dashboard() + } + + addDashboard(dashboard: Dashboard) { + this.dashboards.push(dashboard) + } + + removeDashboard(dashboard: Dashboard) { + this.dashboards = this.dashboards.filter(d => d !== dashboard) + } + + getDashboard(dashboardId: string) { + return this.dashboards.find(d => d.dashboardId === dashboardId) + } + + getDashboardIndex(dashboardId: string) { + return this.dashboards.findIndex(d => d.dashboardId === dashboardId) + } + + getDashboardByIndex(index: number) { + return this.dashboards[index] + } + + getDashboardCount() { + return this.dashboards.length + } + + getDashboardIndexByDashboardId(dashboardId: string) { + return this.dashboards.findIndex(d => d.dashboardId === dashboardId) + } + + updateDashboard(dashboard: Dashboard) { + const index = this.dashboards.findIndex(d => d.dashboardId === dashboard.dashboardId) + if (index >= 0) { + this.dashboards[index] = dashboard + } + } + + selectDashboardById = (dashboardId: any) => { + this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || null;; + } + + setSiteId = (siteId: any) => { + this.siteId = siteId + } + + selectDefaultDashboard = () => { + const pinnedDashboard = this.dashboards.find(d => d.isPinned) + if (pinnedDashboard) { + this.selectedDashboard = pinnedDashboard + } else { + this.selectedDashboard = this.dashboards[0] + } + } +} + +function getRandomWidget() { + const widget = new Widget(); + widget.widgetId = Math.floor(Math.random() * 100); + widget.name = "Widget " + Math.floor(Math.random() * 100); + widget.type = "random"; + widget.colSpan = Math.floor(Math.random() * 2) + 1; + return widget; +} + +function getRandomDashboard(id: any = null) { + const dashboard = new Dashboard(); + dashboard.name = "Random Dashboard"; + dashboard.dashboardId = id ? id : "random-dashboard-" + Math.floor(Math.random() * 10); + for (let i = 0; i < 10; i++) { + const widget = getRandomWidget(); + widget.position = i; + dashboard.addWidget(widget); + } + return dashboard; +} + +const sampleDashboards = [ + getRandomDashboard(12), + getRandomDashboard(), + getRandomDashboard(), + getRandomDashboard(), + getRandomDashboard(), + getRandomDashboard(), +] \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/index.ts b/frontend/app/components/Dashboard/store/index.ts new file mode 100644 index 000000000..bc920972d --- /dev/null +++ b/frontend/app/components/Dashboard/store/index.ts @@ -0,0 +1 @@ +export { default } from './dashboardStore' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/store.tsx b/frontend/app/components/Dashboard/store/store.tsx new file mode 100644 index 000000000..64e1944b9 --- /dev/null +++ b/frontend/app/components/Dashboard/store/store.tsx @@ -0,0 +1,15 @@ +import React from 'react' + +const StoreContext = React.createContext(null) + +export const DashboardStoreProvider = ({ children, store }) => { + return ( + {children} + ); +}; + +export const useDashboardStore = () => React.useContext(StoreContext); + +export const withDashboardStore = (Component) => (props) => { + return ; +}; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/widget.ts b/frontend/app/components/Dashboard/store/widget.ts new file mode 100644 index 000000000..6326dd6db --- /dev/null +++ b/frontend/app/components/Dashboard/store/widget.ts @@ -0,0 +1,61 @@ +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" + +export default class Widget { + widgetId: any = undefined + name: string = "" + type: string = "" + position: number = 0 + data: any = {} + isLoading: boolean = false + isValid: boolean = false + dashboardId: any = undefined + colSpan: number = 2 + + constructor() { + makeAutoObservable(this, { + widgetId: observable, + name: observable, + type: observable, + position: observable, + data: observable, + isLoading: observable, + isValid: observable, + dashboardId: observable, + colSpan: observable, + + fromJson: action, + toJson: action, + validate: action, + update: action, + }) + } + + fromJson(json: any) { + runInAction(() => { + this.widgetId = json.widgetId + this.name = json.name + this.type = json.type + this.data = json.data + }) + return this + } + + toJson() { + return { + widgetId: this.widgetId, + name: this.name, + type: this.type, + data: this.data + } + } + + validate() { + this.isValid = this.name.length > 0 + } + + update(data: any) { + runInAction(() => { + Object.assign(this, data) + }) + } +} \ No newline at end of file diff --git a/frontend/app/components/Header/Header.js b/frontend/app/components/Header/Header.js index 6d541fca7..9b8819cdc 100644 --- a/frontend/app/components/Header/Header.js +++ b/frontend/app/components/Header/Header.js @@ -104,7 +104,7 @@ const Header = (props) => { className={ styles.nav } activeClassName={ styles.active } > - { 'Metrics' } + { 'Dashboard' }
diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index 60760504f..96a527f14 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -5,12 +5,18 @@ import { Provider } from 'react-redux'; import store from './store'; import Router from './Router'; +import DashboardStore from './components/Dashboard/store'; +import { DashboardStoreProvider } from './components/Dashboard/store/store'; + document.addEventListener('DOMContentLoaded', () => { + const dashboardStore = new DashboardStore(); render( ( - + + + ), document.getElementById('app'), diff --git a/frontend/app/routes.js b/frontend/app/routes.js index f9987d49c..3ee5b6a41 100644 --- a/frontend/app/routes.js +++ b/frontend/app/routes.js @@ -100,7 +100,9 @@ export const testBuilderNew = () => '/test-builder'; export const testBuilder = (testId = ':testId') => `/test-builder/${ testId }`; -export const dashboard = () => '/metrics'; +export const dashboard = () => '/dashboard'; +export const dashboardSelected = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }`, hash); +export const dashboardMetric = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash); export const RESULTS_QUERY_KEY = 'results'; export const METRICS_QUERY_KEY = 'metrics'; From 391189e894d2d17f5cdbd26d49cf344130defe66 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 18 Mar 2022 15:40:29 +0100 Subject: [PATCH 03/87] feat(ui) - dashboards wip --- .../app/components/Dashboard/NewDashboard.tsx | 11 +++-- .../DashboardSideMenu/DashboardSideMenu.tsx | 48 +++++++++++++++++++ .../components/DashboardSideMenu/index.ts | 1 + .../DashboardView/DashboardView.tsx | 10 ++-- .../Dashboard/store/dashboardStore.ts | 14 +++--- .../app/components/ui/PageTitle/PageTitle.tsx | 12 +++++ frontend/app/components/ui/PageTitle/index.ts | 1 + .../ui/SideMenuHeader/SideMenuHeader.tsx | 14 ++++++ .../app/components/ui/SideMenuHeader/index.ts | 1 + .../ui/SideMenuHeader/sideMenuHeader.css | 4 ++ .../ui/SideMenuitem/SideMenuitem.js | 12 +++-- .../ui/SideMenuitem/sideMenuItem.css | 1 - frontend/app/components/ui/index.js | 2 + frontend/app/svg/icons/bar-chart-line.svg | 3 ++ 14 files changed, 114 insertions(+), 20 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardSideMenu/index.ts create mode 100644 frontend/app/components/ui/PageTitle/PageTitle.tsx create mode 100644 frontend/app/components/ui/PageTitle/index.ts create mode 100644 frontend/app/components/ui/SideMenuHeader/SideMenuHeader.tsx create mode 100644 frontend/app/components/ui/SideMenuHeader/index.ts create mode 100644 frontend/app/components/ui/SideMenuHeader/sideMenuHeader.css create mode 100644 frontend/app/svg/icons/bar-chart-line.svg diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 252e8bc53..11b2313e5 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -1,11 +1,12 @@ import React, { useEffect } from 'react'; import { Switch, Route, Redirect } from 'react-router'; import withPageTitle from 'HOCs/withPageTitle'; -import { observer } from "mobx-react-lite"; +import { observer, useObserver } from "mobx-react-lite"; import { withDashboardStore } from './store/store'; import { withRouter } from 'react-router-dom'; import DashboardView from './components/DashboardView'; import { dashboardSelected, dashboardMetric, withSiteId } from 'App/routes'; +import DashboardSideMenu from './components/DashboardSideMenu'; function NewDashboard(props) { const { store, match: { params: { siteId, dashboardId, metricId } } } = props; @@ -20,10 +21,10 @@ function NewDashboard(props) { } }, [dashboardId]); - return ( + return useObserver(() => (
- MENU +
@@ -37,9 +38,9 @@ function NewDashboard(props) {
- ); + )); } export default withPageTitle('New Dashboard')( - withRouter(withDashboardStore(observer(NewDashboard))) + withRouter(withDashboardStore(NewDashboard)) ); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx new file mode 100644 index 000000000..850423c66 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -0,0 +1,48 @@ +import { useObserver } from 'mobx-react-lite'; +import React from 'react'; +import { SideMenuitem, SideMenuHeader } from 'UI'; +import { withDashboardStore } from '../../store/store'; + +function DashboardSideMenu(props) { + const { store } = props; + const { selectedDashboard } = store; + + const onItemClick = (dashboard) => { + store.selectDashboardById(dashboard.dashboardId); + }; + + return useObserver(() => ( +
+ + {store.dashboards.map(item => ( + onItemClick(item)} + /> + ))} +
+
+ setShowAlerts(true)} + /> +
+
+
+ setShowAlerts(true)} + /> +
+
+ )); +} + +export default withDashboardStore(DashboardSideMenu); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/index.ts b/frontend/app/components/Dashboard/components/DashboardSideMenu/index.ts new file mode 100644 index 000000000..7d83f5bed --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardSideMenu'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index 850726789..ce56053d8 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -2,19 +2,23 @@ import React from 'react'; import WidgetWrapper from '../../WidgetWrapper'; import { observer } from 'mobx-react-lite'; import { withDashboardStore } from '../../store/store'; +import { Button, PageTitle } from 'UI'; function DashboardView(props) { const { store } = props; const dashboard = store.selectedDashboard const list = dashboard?.widgets; - return dashboard ? ( + return (
- test {dashboard.dashboardId} +
+ + +
{list && list.map(item => )}
- ) :

Loading...

; + ) } export default withDashboardStore(observer(DashboardView)); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 1227b5611..0fc775e1e 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -5,7 +5,7 @@ import Widget from "./widget"; export default class DashboardStore { dashboards: Dashboard[] = [] - selectedDashboard: Dashboard | null = null + selectedDashboard: Dashboard | null = new Dashboard() isLoading: boolean = false siteId: any = null @@ -194,10 +194,14 @@ export default class DashboardStore { selectDefaultDashboard = () => { const pinnedDashboard = this.dashboards.find(d => d.isPinned) - if (pinnedDashboard) { - this.selectedDashboard = pinnedDashboard + if (this.dashboards.length > 0) { + if (pinnedDashboard) { + this.selectedDashboard = pinnedDashboard + } else { + this.selectedDashboard = this.dashboards[0] + } } else { - this.selectedDashboard = this.dashboards[0] + this.selectedDashboard = new Dashboard() } } } @@ -228,6 +232,4 @@ const sampleDashboards = [ getRandomDashboard(), getRandomDashboard(), getRandomDashboard(), - getRandomDashboard(), - getRandomDashboard(), ] \ No newline at end of file diff --git a/frontend/app/components/ui/PageTitle/PageTitle.tsx b/frontend/app/components/ui/PageTitle/PageTitle.tsx new file mode 100644 index 000000000..6ee102c0d --- /dev/null +++ b/frontend/app/components/ui/PageTitle/PageTitle.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import cn from 'classnames'; + +function PageTitle({ title, className = '' }) { + return ( +

+ {title} +

+ ); +} + +export default PageTitle; \ No newline at end of file diff --git a/frontend/app/components/ui/PageTitle/index.ts b/frontend/app/components/ui/PageTitle/index.ts new file mode 100644 index 000000000..6c6e0d2c8 --- /dev/null +++ b/frontend/app/components/ui/PageTitle/index.ts @@ -0,0 +1 @@ +export { default } from './PageTitle'; \ No newline at end of file diff --git a/frontend/app/components/ui/SideMenuHeader/SideMenuHeader.tsx b/frontend/app/components/ui/SideMenuHeader/SideMenuHeader.tsx new file mode 100644 index 000000000..2a5ccd6fa --- /dev/null +++ b/frontend/app/components/ui/SideMenuHeader/SideMenuHeader.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import cn from 'classnames'; +import stl from './sideMenuHeader.css'; + +function SideMenuHeader(props) { + const { text, className } = props; + return ( +
+ { text } +
+ ) +} + +export default SideMenuHeader; \ No newline at end of file diff --git a/frontend/app/components/ui/SideMenuHeader/index.ts b/frontend/app/components/ui/SideMenuHeader/index.ts new file mode 100644 index 000000000..b61e683de --- /dev/null +++ b/frontend/app/components/ui/SideMenuHeader/index.ts @@ -0,0 +1 @@ +export { default } from './SideMenuHeader'; \ No newline at end of file diff --git a/frontend/app/components/ui/SideMenuHeader/sideMenuHeader.css b/frontend/app/components/ui/SideMenuHeader/sideMenuHeader.css new file mode 100644 index 000000000..5dce4e250 --- /dev/null +++ b/frontend/app/components/ui/SideMenuHeader/sideMenuHeader.css @@ -0,0 +1,4 @@ +.label { + letter-spacing: 0.2em; + color: gray; +} \ No newline at end of file diff --git a/frontend/app/components/ui/SideMenuitem/SideMenuitem.js b/frontend/app/components/ui/SideMenuitem/SideMenuitem.js index f1649934b..978e02fbd 100644 --- a/frontend/app/components/ui/SideMenuitem/SideMenuitem.js +++ b/frontend/app/components/ui/SideMenuitem/SideMenuitem.js @@ -3,7 +3,7 @@ import { Icon, Popup } from 'UI'; import cn from 'classnames'; import stl from './sideMenuItem.css'; -function SideMenuitem({ iconBg = false, iconColor = "gray-dark", iconSize = 18, className, iconName = 'info', title, active = false, disabled = false, onClick, deleteHandler, ...props }) { +function SideMenuitem({ iconBg = false, iconColor = "gray-dark", iconSize = 18, className, iconName = null, title, active = false, disabled = false, onClick, deleteHandler, ...props }) { return (
-
-
- -
+ { iconName && ( +
+
+ +
+ )} { title }
{deleteHandler && diff --git a/frontend/app/components/ui/SideMenuitem/sideMenuItem.css b/frontend/app/components/ui/SideMenuitem/sideMenuItem.css index 6b7d0743e..f666a1477 100644 --- a/frontend/app/components/ui/SideMenuitem/sideMenuItem.css +++ b/frontend/app/components/ui/SideMenuitem/sideMenuItem.css @@ -28,7 +28,6 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - margin-left: 10px; margin-top: 1px; } diff --git a/frontend/app/components/ui/index.js b/frontend/app/components/ui/index.js index 1e0088720..d770c5aa1 100644 --- a/frontend/app/components/ui/index.js +++ b/frontend/app/components/ui/index.js @@ -55,5 +55,7 @@ export { default as HighlightCode } from './HighlightCode'; export { default as NoPermission } from './NoPermission'; export { default as NoSessionPermission } from './NoSessionPermission'; export { default as HelpText } from './HelpText'; +export { default as SideMenuHeader } from './SideMenuHeader'; +export { default as PageTitle } from './PageTitle'; export { Input, Modal, Form, Message, Card } from 'semantic-ui-react'; diff --git a/frontend/app/svg/icons/bar-chart-line.svg b/frontend/app/svg/icons/bar-chart-line.svg new file mode 100644 index 000000000..e3f0cf255 --- /dev/null +++ b/frontend/app/svg/icons/bar-chart-line.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file From d7bd56f79eaf7db1faa088eccf22a1e169a56067 Mon Sep 17 00:00:00 2001 From: Mehdi Osman Date: Fri, 18 Mar 2022 17:21:49 +0100 Subject: [PATCH 04/87] Replaced Slack with Discord --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b943773e6..870d47fcc 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ For those who want to simply use OpenReplay as a service, [sign up](https://app. Please refer to the [official OpenReplay documentation](https://docs.openreplay.com/). That should help you troubleshoot common issues. For additional help, you can reach out to us on one of these channels: -- [Slack](https://slack.openreplay.com) (Connect with our engineers and community) +- [Discord](https://discord.openreplay.com) (Connect with our engineers and community) - [GitHub](https://github.com/openreplay/openreplay/issues) (Bug and issue reports) - [Twitter](https://twitter.com/OpenReplayHQ) (Product updates, Great content) - [Website chat](https://openreplay.com) (Talk to us) @@ -80,7 +80,7 @@ We're always on the lookout for contributions to OpenReplay, and we're glad you' See our [Contributing Guide](CONTRIBUTING.md) for more details. -Also, feel free to join our [Slack](https://slack.openreplay.com) to ask questions, discuss ideas or connect with our contributors. +Also, feel free to join our [Discord](https://discord.openreplay.com) to ask questions, discuss ideas or connect with our contributors. ## Roadmap From b2566d158b4cb22e5a81ea92ab5c1c685450234f Mon Sep 17 00:00:00 2001 From: Rajesh Rajendran Date: Sat, 19 Mar 2022 16:49:48 +0000 Subject: [PATCH 05/87] Update third-party.md --- third-party.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/third-party.md b/third-party.md index 0f20d925d..ec537824e 100644 --- a/third-party.md +++ b/third-party.md @@ -1,4 +1,4 @@ -## Licenses (as of February 23, 2022) +## Licenses (as of March 19, 2022) Below is the list of dependencies used in OpenReplay software. Licenses may change between versions, so please keep this up to date with every new library you use. @@ -95,3 +95,11 @@ Below is the list of dependencies used in OpenReplay software. Licenses may chan | geoip-lite | Apache2 | JavaScript | | ua-parser-js | MIT | JavaScript | | express | MIT | JavaScript | +| kafka | Apache2 | Java | +| stern | Apache2 | Go | +| k9s | Apache2 | Go | +| Minio | General Public License v3.0 | Go | +| PostgreSQL | PostgreSQL License | c++ | +| Ansible | GPL-3.0 | python | +| k3s | Apache2 | Go | +| Nginx | BSD-2-Clause | c | From 116e06438c60a4bc803a0a8a78cfa844144fab6c Mon Sep 17 00:00:00 2001 From: Rajesh Rajendran Date: Sat, 19 Mar 2022 16:52:50 +0000 Subject: [PATCH 06/87] Update third-party.md --- third-party.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/third-party.md b/third-party.md index ec537824e..d02394e64 100644 --- a/third-party.md +++ b/third-party.md @@ -99,7 +99,8 @@ Below is the list of dependencies used in OpenReplay software. Licenses may chan | stern | Apache2 | Go | | k9s | Apache2 | Go | | Minio | General Public License v3.0 | Go | -| PostgreSQL | PostgreSQL License | c++ | -| Ansible | GPL-3.0 | python | +| PostgreSQL | PostgreSQL License | C++ | +| Ansible | GPL-3.0 | Python | | k3s | Apache2 | Go | -| Nginx | BSD-2-Clause | c | +| Nginx | BSD-2-Clause | C | +| Clickhouse | Apache License 2.0 | C++ | From 38c8aae7106368addb887b334ba63131f684578e Mon Sep 17 00:00:00 2001 From: Mehdi Osman Date: Sat, 19 Mar 2022 17:57:41 +0100 Subject: [PATCH 07/87] Updated scope (infrastructure) --- third-party.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/third-party.md b/third-party.md index d02394e64..e5220ac78 100644 --- a/third-party.md +++ b/third-party.md @@ -95,12 +95,12 @@ Below is the list of dependencies used in OpenReplay software. Licenses may chan | geoip-lite | Apache2 | JavaScript | | ua-parser-js | MIT | JavaScript | | express | MIT | JavaScript | -| kafka | Apache2 | Java | -| stern | Apache2 | Go | -| k9s | Apache2 | Go | -| Minio | General Public License v3.0 | Go | -| PostgreSQL | PostgreSQL License | C++ | -| Ansible | GPL-3.0 | Python | -| k3s | Apache2 | Go | -| Nginx | BSD-2-Clause | C | -| Clickhouse | Apache License 2.0 | C++ | +| kafka | Apache2 | Infrastructure | +| stern | Apache2 | Infrastructure | +| k9s | Apache2 | Infrastructure | +| minio | GPLv3 | Infrastructure | +| postgreSQL | PostgreSQL License | Infrastructure | +| ansible | GPLv3 | Infrastructure | +| k3s | Apache2 | Infrastructure | +| nginx | BSD2 | Infrastructure | +| clickhouse | Apache2 | Infrastructure | From 7a8b45743adda66537b5d4ff7ffe71b3b79a91f8 Mon Sep 17 00:00:00 2001 From: Mehdi Osman Date: Sat, 19 Mar 2022 18:28:26 +0100 Subject: [PATCH 08/87] Update third-party.md --- third-party.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party.md b/third-party.md index e5220ac78..4dbff4a80 100644 --- a/third-party.md +++ b/third-party.md @@ -6,7 +6,7 @@ Below is the list of dependencies used in OpenReplay software. Licenses may chan |----------|-------------|-------------| | btcutil | IST | Go | | confluent-kafka-go | Apache2 | Go | -| UUID | BSD3 | Go | +| uuid | BSD3 | Go | | pgconn | MIT | Go | | pgx | MIT | Go | | pgerrcode | MIT | Go | From 5dbb1eeaf154fb404a8b1f6358b609439946ee47 Mon Sep 17 00:00:00 2001 From: estradino Date: Sat, 19 Mar 2022 22:42:55 +0100 Subject: [PATCH 09/87] :broom: Updated third-party list --- third-party.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/third-party.md b/third-party.md index 4dbff4a80..580e4c576 100644 --- a/third-party.md +++ b/third-party.md @@ -6,9 +6,13 @@ Below is the list of dependencies used in OpenReplay software. Licenses may chan |----------|-------------|-------------| | btcutil | IST | Go | | confluent-kafka-go | Apache2 | Go | +| compress | Apache2 | Go | | uuid | BSD3 | Go | +| mux | BSD3 | Go | +| lib/pq | MIT | Go | | pgconn | MIT | Go | | pgx | MIT | Go | +| go-redis | BSD2 | Go | | pgerrcode | MIT | Go | | pgzip | MIT | Go | | maxminddb-golang | IST | Go | @@ -90,7 +94,6 @@ Below is the list of dependencies used in OpenReplay software. Licenses may chan | source-map | BSD3 | JavaScript | | aws-sdk | Apache2 | JavaScript | | serverless | MIT | JavaScript | -| lib/pq | MIT | Go | | peerjs | MIT | JavaScript | | geoip-lite | Apache2 | JavaScript | | ua-parser-js | MIT | JavaScript | @@ -104,3 +107,4 @@ Below is the list of dependencies used in OpenReplay software. Licenses may chan | k3s | Apache2 | Infrastructure | | nginx | BSD2 | Infrastructure | | clickhouse | Apache2 | Infrastructure | +| redis | BSD3 | Infrastructure | \ No newline at end of file From 94370f028f09ccab9f29c15b2ec5f9a0298c1c68 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 21 Mar 2022 12:28:27 +0100 Subject: [PATCH 10/87] feat(ui) - dashboards wip --- frontend/app/Router.js | 16 ++++- .../app/components/Dashboard/NewDashboard.tsx | 61 ++++++++++++------- .../Dashboard/SideMenu/SideMenuSection.js | 2 +- .../Dashboard/WidgetView/WidgetView.tsx | 13 +++- .../Dashboard/WidgetWrapper/WidgetWrapper.tsx | 30 +++++---- .../DashboardSideMenu/DashboardSideMenu.tsx | 36 +++++++---- .../DashboardView/DashboardView.tsx | 5 +- .../components/Dashboard/store/dashboard.ts | 3 +- .../Dashboard/store/dashboardStore.ts | 51 +++++++++++----- .../app/components/Dashboard/store/widget.ts | 2 +- .../app/components/ui/ItemMenu/ItemMenu.js | 21 +++++-- .../app/components/ui/ItemMenu/itemMenu.css | 2 +- .../ui/SideMenuitem/SideMenuitem.js | 32 +++++++--- .../ui/SideMenuitem/sideMenuItem.css | 10 ++- frontend/app/routes.js | 26 +++++++- frontend/app/svg/icons/pin-fill.svg | 3 + 16 files changed, 223 insertions(+), 90 deletions(-) create mode 100644 frontend/app/svg/icons/pin-fill.svg diff --git a/frontend/app/Router.js b/frontend/app/Router.js index 665ff4df6..a6a9ba090 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -48,8 +48,13 @@ const FunnelIssue = withSiteIdUpdater(FunnelIssueDetails); const withSiteId = routes.withSiteId; const withObTab = routes.withObTab; -const DASHBOARD_PATH = routes.dashboardSelected(); -const WIDGET_PATAH = routes.dashboardMetric(); +const DASHBOARD_PATH = routes.dashboard(); +// const DASHBOARD_WIDGET_CREATE_PATH = routes.dashboardMetricCreate(); +// const DASHBOARD_WIDGET_DETAILS_PATH = routes.dashboardMetricDetails(); +// const METRIC_CREATE_PATH = routes.metricCreate(); +// const METRIC_DETAILS_PATH = routes.metricDetails(); + +// const WIDGET_PATAH = routes.dashboardMetric(); const SESSIONS_PATH = routes.sessions(); const ASSIST_PATH = routes.assist(); const ERRORS_PATH = routes.errors(); @@ -182,8 +187,13 @@ class Router extends React.Component { { siteIdList.length === 0 && } - + + {/* + + + */} + diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 11b2313e5..831e44bd8 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -1,46 +1,63 @@ import React, { useEffect } from 'react'; import { Switch, Route, Redirect } from 'react-router'; import withPageTitle from 'HOCs/withPageTitle'; -import { observer, useObserver } from "mobx-react-lite"; -import { withDashboardStore } from './store/store'; +import { observer } from "mobx-react-lite"; +import { useDashboardStore } from './store/store'; import { withRouter } from 'react-router-dom'; import DashboardView from './components/DashboardView'; -import { dashboardSelected, dashboardMetric, withSiteId } from 'App/routes'; +import { dashboardSelected, dashboardMetricDetails, dashboardMetricCreate, withSiteId } from 'App/routes'; import DashboardSideMenu from './components/DashboardSideMenu'; +import WidgetView from './WidgetView'; function NewDashboard(props) { - const { store, match: { params: { siteId, dashboardId, metricId } } } = props; + const { match: { params: { siteId, dashboardId, metricId } } } = props; + const store: any = useDashboardStore(); const dashboard = store.selectedDashboard; useEffect(() => { store.setSiteId(siteId); - if (dashboardId) { - store.selectDashboardById(dashboardId); - } else { - store.selectDefaultDashboard(); - } - }, [dashboardId]); + }, []); - return useObserver(() => ( + useEffect(() => { + console.log('dashboardId', dashboardId); + if (!dashboard || !dashboard.dashboardId) { + if (dashboardId) { + store.selectDashboardById(dashboardId); + } else { + store.selectDefaultDashboard(); + } + } + + }, [dashboard]); + + return (
- - - - - -

Metric

-
- -
+ { dashboard && dashboard.dashboardId && ( + + + + + + + + + + + {/* + + */} + + + )}
- )); + ); } export default withPageTitle('New Dashboard')( - withRouter(withDashboardStore(NewDashboard)) + withRouter(observer(NewDashboard)) ); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js b/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js index e26e24da2..801c02415 100644 --- a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js +++ b/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js @@ -22,7 +22,7 @@ function SideMenuSection({ title, items, onItemClick, setShowAlerts, siteId }) { )}
-
+
- Widget view +
+
+

{widget.name}

+
+
); } -export default WidgetView; \ No newline at end of file +export default withRouter(WidgetView); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx index 35e088d52..daade0774 100644 --- a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx @@ -1,9 +1,7 @@ import React from 'react'; -import { observer } from "mobx-react-lite"; import { useDashboardStore } from '../store/store'; import cn from 'classnames'; -import { Link } from 'UI'; -import { dashboardMetric, withSiteId } from 'App/routes'; +import { ItemMenu } from 'UI'; function WidgetWrapper(props) { const { widget } = props; @@ -12,23 +10,33 @@ function WidgetWrapper(props) { const siteId = store.siteId; return ( -
- -
+
+ {/* */} +
{widget.name} - {widget.position}
- + */}
-
+
- + {/* */}
); } -export default observer(WidgetWrapper); \ No newline at end of file +export default WidgetWrapper; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 850423c66..4c984904e 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -1,39 +1,51 @@ -import { useObserver } from 'mobx-react-lite'; +import { useObserver, observer, useLocalObservable } from 'mobx-react-lite'; import React from 'react'; -import { SideMenuitem, SideMenuHeader } from 'UI'; +import { SideMenuitem, SideMenuHeader, Icon } from 'UI'; import { withDashboardStore } from '../../store/store'; +import { withRouter } from 'react-router-dom'; +import { withSiteId, dashboardSelected } from 'App/routes'; function DashboardSideMenu(props) { - const { store } = props; - const { selectedDashboard } = store; + const { store, history } = props; + const { dashboardId } = store.selectedDashboard; const onItemClick = (dashboard) => { store.selectDashboardById(dashboard.dashboardId); + const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(store.siteId)); + // console.log('path', path); + // history.push(path); }; - return useObserver(() => ( + return (
{store.dashboards.map(item => ( onItemClick(item)} + + leading = {( +
+
+ {item.isPinned &&
} +
+ )} /> ))}
-
+
setShowAlerts(true)} - /> + />
-
+
- )); + ); } -export default withDashboardStore(DashboardSideMenu); \ No newline at end of file +export default withDashboardStore(withRouter(observer(DashboardSideMenu))); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index ce56053d8..e0d45a531 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -2,7 +2,8 @@ import React from 'react'; import WidgetWrapper from '../../WidgetWrapper'; import { observer } from 'mobx-react-lite'; import { withDashboardStore } from '../../store/store'; -import { Button, PageTitle } from 'UI'; +import { Button, PageTitle, Link } from 'UI'; +import { withSiteId, dashboardMetricCreate } from 'App/routes'; function DashboardView(props) { const { store } = props; @@ -12,7 +13,7 @@ function DashboardView(props) {
- +
{list && list.map(item => )} diff --git a/frontend/app/components/Dashboard/store/dashboard.ts b/frontend/app/components/Dashboard/store/dashboard.ts index 9395b7c1b..b8113f3ba 100644 --- a/frontend/app/components/Dashboard/store/dashboard.ts +++ b/frontend/app/components/Dashboard/store/dashboard.ts @@ -1,4 +1,4 @@ -import { makeAutoObservable, makeObservable, observable, action, runInAction, computed, reaction } from "mobx" +import { makeAutoObservable, observable, action, runInAction } from "mobx" import Widget from "./widget" // import APIClient from 'App/api_client'; @@ -9,6 +9,7 @@ export default class Dashboard { widgets: Widget[] = [] isValid: boolean = false isPinned: boolean = false + currentWidget: Widget = new Widget() constructor() { makeAutoObservable(this, { diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 0fc775e1e..832988ce9 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -8,6 +8,7 @@ export default class DashboardStore { selectedDashboard: Dashboard | null = new Dashboard() isLoading: boolean = false siteId: any = null + currentWidget: Widget = new Widget() private client = new APIClient() @@ -25,7 +26,8 @@ export default class DashboardStore { getDashboardByIndex: action, getDashboardCount: action, getDashboardIndexByDashboardId: action, - selectDashboardById: action, + selectDashboardById: action, + selectDefaultDashboard: action, toJson: action, fromJson: action, setSiteId: action, @@ -34,7 +36,7 @@ export default class DashboardStore { // TODO remove this sample data this.dashboards = sampleDashboards - this.selectedDashboard = sampleDashboards[0] + // this.selectedDashboard = sampleDashboards[0] // setInterval(() => { // this.selectedDashboard?.addWidget(getRandomWidget()) @@ -185,7 +187,7 @@ export default class DashboardStore { } selectDashboardById = (dashboardId: any) => { - this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || null;; + this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || new Dashboard(); } setSiteId = (siteId: any) => { @@ -193,15 +195,13 @@ export default class DashboardStore { } selectDefaultDashboard = () => { - const pinnedDashboard = this.dashboards.find(d => d.isPinned) if (this.dashboards.length > 0) { + const pinnedDashboard = this.dashboards.find(d => d.isPinned) if (pinnedDashboard) { this.selectedDashboard = pinnedDashboard } else { this.selectedDashboard = this.dashboards[0] } - } else { - this.selectedDashboard = new Dashboard() } } } @@ -209,17 +209,38 @@ export default class DashboardStore { function getRandomWidget() { const widget = new Widget(); widget.widgetId = Math.floor(Math.random() * 100); - widget.name = "Widget " + Math.floor(Math.random() * 100); + widget.name = randomMetricName(); widget.type = "random"; widget.colSpan = Math.floor(Math.random() * 2) + 1; return widget; } -function getRandomDashboard(id: any = null) { +function generateRandomPlaceName() { + const placeNames = [ + "New York", + "Los Angeles", + "Chicago", + "Houston", + "Philadelphia", + "Phoenix", + "San Antonio", + "San Diego", + ] + return placeNames[Math.floor(Math.random() * placeNames.length)] +} + + +function randomMetricName () { + const metrics = ["Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders"]; + return metrics[Math.floor(Math.random() * metrics.length)]; +} + +function getRandomDashboard(id: any = null, isPinned = false) { const dashboard = new Dashboard(); - dashboard.name = "Random Dashboard"; - dashboard.dashboardId = id ? id : "random-dashboard-" + Math.floor(Math.random() * 10); - for (let i = 0; i < 10; i++) { + dashboard.name = generateRandomPlaceName(); + dashboard.dashboardId = id ? id : Math.floor(Math.random() * 10); + dashboard.isPinned = isPinned; + for (let i = 0; i < 8; i++) { const widget = getRandomWidget(); widget.position = i; dashboard.addWidget(widget); @@ -228,8 +249,8 @@ function getRandomDashboard(id: any = null) { } const sampleDashboards = [ - getRandomDashboard(12), - getRandomDashboard(), - getRandomDashboard(), - getRandomDashboard(), + getRandomDashboard(1, true), + getRandomDashboard(2), + getRandomDashboard(3), + getRandomDashboard(4), ] \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/widget.ts b/frontend/app/components/Dashboard/store/widget.ts index 6326dd6db..2cd1bee32 100644 --- a/frontend/app/components/Dashboard/store/widget.ts +++ b/frontend/app/components/Dashboard/store/widget.ts @@ -2,7 +2,7 @@ import { makeAutoObservable, runInAction, observable, action, reaction } from "m export default class Widget { widgetId: any = undefined - name: string = "" + name: string = "New Metric" type: string = "" position: number = 0 data: any = {} diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.js b/frontend/app/components/ui/ItemMenu/ItemMenu.js index c31130071..94274a405 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.js +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.js @@ -39,13 +39,22 @@ export default class ItemMenu extends React.PureComponent { return (
-
{ this.menuBtnRef = ref; } } className={ styles.menuBtn } onClick={ this.toggleMenu } role="button" tabIndex="-1" - /> + /> */} +
{ this.menuBtnRef = ref; } } + className="w-10 h-10 cursor-pointer bg-white rounded-full flex items-center justify-center hover:bg-gray-lightest" + onClick={ this.toggleMenu } + role="button" + tabIndex="-1" + > + +
-
- -
+ { icon && ( +
+ +
+ )}
{ text }
))} diff --git a/frontend/app/components/ui/ItemMenu/itemMenu.css b/frontend/app/components/ui/ItemMenu/itemMenu.css index bb9bd6426..eabfb050a 100644 --- a/frontend/app/components/ui/ItemMenu/itemMenu.css +++ b/frontend/app/components/ui/ItemMenu/itemMenu.css @@ -6,7 +6,7 @@ } .menuBtn { - @mixin icon-before ellipsis-v, $gray-darkest, 25px { + @mixin icon-before ellipsis-v, $gray-darkest, 18px { margin: 5px; } width: 36px; diff --git a/frontend/app/components/ui/SideMenuitem/SideMenuitem.js b/frontend/app/components/ui/SideMenuitem/SideMenuitem.js index 978e02fbd..dbeca551e 100644 --- a/frontend/app/components/ui/SideMenuitem/SideMenuitem.js +++ b/frontend/app/components/ui/SideMenuitem/SideMenuitem.js @@ -3,7 +3,20 @@ import { Icon, Popup } from 'UI'; import cn from 'classnames'; import stl from './sideMenuItem.css'; -function SideMenuitem({ iconBg = false, iconColor = "gray-dark", iconSize = 18, className, iconName = null, title, active = false, disabled = false, onClick, deleteHandler, ...props }) { +function SideMenuitem({ + iconBg = false, + iconColor = "gray-dark", + iconSize = 18, + className, + iconName = null, + title, + active = false, + disabled = false, + onClick, + deleteHandler, + leading = null, + ...props + }) { return ( -
+
+
{ iconName && ( -
-
- -
- )} - { title } +
+
+ +
+ )} + { title } +
+ { leading && leading }
{deleteHandler &&
diff --git a/frontend/app/components/ui/SideMenuitem/sideMenuItem.css b/frontend/app/components/ui/SideMenuitem/sideMenuItem.css index f666a1477..e0780c68e 100644 --- a/frontend/app/components/ui/SideMenuitem/sideMenuItem.css +++ b/frontend/app/components/ui/SideMenuitem/sideMenuItem.css @@ -5,10 +5,14 @@ cursor: pointer; &:hover { - color: $teal; - & svg { - fill: $teal; + & .iconLabel { + color: $teal; + + & svg { + fill: $teal; + } } + & .actions { opacity: 1; } diff --git a/frontend/app/routes.js b/frontend/app/routes.js index 3ee5b6a41..8693ae3e6 100644 --- a/frontend/app/routes.js +++ b/frontend/app/routes.js @@ -102,14 +102,34 @@ export const testBuilder = (testId = ':testId') => `/test-builder/${ testId }`; export const dashboard = () => '/dashboard'; export const dashboardSelected = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }`, hash); -export const dashboardMetric = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash); + +export const dashboardMetricDetails = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash); +export const dashboardMetricCreate = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }/metric/create`, hash); +export const metricCreate = () => `/metric/create`; +export const metricDetails = (id = ':metricId', hash) => hashed(`/metric/${ id }`, hash); + export const RESULTS_QUERY_KEY = 'results'; export const METRICS_QUERY_KEY = 'metrics'; export const SOURCE_QUERY_KEY = 'source'; export const WIDGET_QUERY_KEY = 'widget'; -const REQUIRED_SITE_ID_ROUTES = [ liveSession(''), session(''), sessions(), assist(), dashboard(''), error(''), errors(), onboarding(''), funnel(''), funnelIssue(''), ]; +const REQUIRED_SITE_ID_ROUTES = [ + liveSession(''), + session(''), + sessions(), + assist(), + dashboard(''), + dashboardSelected(''), + // dashboardMetricCreate(''), + dashboardMetricDetails(''), + metricCreate(''), + error(''), + errors(), + onboarding(''), + funnel(''), + funnelIssue(''), + ]; const routeNeedsSiteId = path => REQUIRED_SITE_ID_ROUTES.some(r => path.startsWith(r)); const siteIdToUrl = (siteId = ':siteId') => { if (Array.isArray(siteId)) { @@ -132,7 +152,7 @@ export function isRoute(route, path){ routeParts.every((p, i) => p.startsWith(':') || p === pathParts[ i ]); } -const SITE_CHANGE_AVALIABLE_ROUTES = [ sessions(), assist(), dashboard(), errors(), onboarding('')]; +const SITE_CHANGE_AVALIABLE_ROUTES = [ sessions(), assist(), dashboard(), dashboardSelected(''), errors(), onboarding('')]; export const siteChangeAvaliable = path => SITE_CHANGE_AVALIABLE_ROUTES.some(r => isRoute(r, path)); export const redirects = Object.entries({ diff --git a/frontend/app/svg/icons/pin-fill.svg b/frontend/app/svg/icons/pin-fill.svg new file mode 100644 index 000000000..b5ea87670 --- /dev/null +++ b/frontend/app/svg/icons/pin-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file From 9675da07e1aa6bc837ccb2c943564e81858adc72 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 22 Mar 2022 17:16:08 +0100 Subject: [PATCH 11/87] feat(api): dashboards 1/5 --- api/chalicelib/core/dashboards2.py | 60 +++++++++++++++++++ api/routers/subs/dashboard.py | 27 ++++++++- api/schemas.py | 9 +++ .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 43 +++++++++++++ 4 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 api/chalicelib/core/dashboards2.py create mode 100644 scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py new file mode 100644 index 000000000..5f2bced2d --- /dev/null +++ b/api/chalicelib/core/dashboards2.py @@ -0,0 +1,60 @@ +import schemas +from chalicelib.utils import helper +from chalicelib.utils import pg_client + + +def create_dashboard(project_id, user_id, data: schemas.CreateDashboardSchema): + with pg_client.PostgresClient() as cur: + pg_query = f"""INSERT INTO dashboards(project_id, user_id, name, is_public, is_pinned) + VALUES(%(projectId)s, %(userId)s, %(name)s, %(is_public)s, %(is_pinned)s) + RETURNING *;""" + params = {"userId": user_id, "projectId": project_id, **data.dict()} + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + +def get_dashboards(project_id, user_id): + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT * + FROM dashboards + WHERE deleted_at ISNULL + AND project_id = %(projectId)s + AND (user_id = %(userId)s OR is_public);""" + params = {"userId": user_id, "projectId": project_id} + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + return helper.list_to_camel_case(rows) + + +def get_dashboard(project_id, user_id, dashboard_id): + with pg_client.PostgresClient() as cur: + pg_query = """SELECT dashboards.*, all_widgets.* + FROM dashboards + LEFT JOIN LATERAL (SELECT COALESCE(ARRAY_AGG(widgets), '{}') AS widgets + FROM widgets + INNER JOIN dashboard_widgets USING (widget_id) + WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id + AND widgets.deleted_at ISNULL + AND (widgets.project_id ISNULL OR widgets.project_id = %(projectId)s) + ) AS all_widgets ON (TRUE) + WHERE dashboards.deleted_at ISNULL + AND dashboards.project_id = %(projectId)s + AND dashboard_id = %(dashboard_id)s + AND (dashboards.user_id = %(userId)s OR is_public);""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id} + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + +def get_widgets(project_id): + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT * + FROM widgets + WHERE deleted_at ISNULL + AND project_id = %(projectId)s;""" + params = {"projectId": project_id} + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + return helper.list_to_camel_case(rows) diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 169893693..1f5e827e4 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -1,9 +1,10 @@ -from fastapi import Body +from fastapi import Body, Depends import schemas -from chalicelib.core import dashboard +from chalicelib.core import dashboard, dashboards2 from chalicelib.core import metadata from chalicelib.utils import helper +from or_dependencies import OR_context from routers.base import get_routers public_app, app, app_apikey = get_routers() @@ -344,3 +345,25 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body *helper.explode_widget(dashboard.get_avg_cpu(project_id=projectId, **data.dict())), *helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())), ]} + + +@app.post('/{projectId}/dashboards', tags=["dashboard", "metrics"]) +@app.put('/{projectId}/dashboards', tags=["dashboard", "metrics"]) +def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)} + + +@app.get('/{projectId}/dashboards', tags=["dashboard", "metrics"]) +def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.get_dashboards(project_id=projectId, user_id=context.user_id)} + + +@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"]) +def get_dashboards(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} + + +@app.get('/{projectId}/widgets', tags=["dashboard", "metrics"]) +def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.get_widgets(project_id=projectId)} diff --git a/api/schemas.py b/api/schemas.py index 77cb78c05..61b1f4056 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -875,3 +875,12 @@ class UpdateCustomMetricsStatusSchema(BaseModel): class SavedSearchSchema(FunnelSchema): filter: FlatSessionsSearchPayloadSchema = Field([]) + + +class CreateDashboardSchema(BaseModel): + name: str = Field(...) + is_public: bool = Field(default=False) + is_pinned: bool = Field(default=False) + + class Config: + alias_generator = attribute_to_camel_case diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql new file mode 100644 index 000000000..a8bb8aafd --- /dev/null +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -0,0 +1,43 @@ +BEGIN; +CREATE OR REPLACE FUNCTION openreplay_version() + RETURNS text AS +$$ +SELECT 'v1.5.5' +$$ LANGUAGE sql IMMUTABLE; + + +CREATE TABLE dashboards +( + dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + name text NOT NULL, + is_public boolean NOT NULL DEFAULT TRUE, + is_pinned boolean NOT NULL DEFAULT FALSE, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + deleted_at timestamp NULL DEFAULT NULL +); + + +CREATE TABLE widgets +( + widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + name text NOT NULL, + is_template boolean NOT NULL DEFAULT FALSE, + predefined boolean NOT NULL DEFAULT FALSE, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + deleted_at timestamp NULL DEFAULT NULL +); + +CREATE TABLE dashboard_widgets +( + dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, + widget_id integer NOT NULL REFERENCES widgets (widget_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + configuration jsonb NOT NULL DEFAULT '{}'::jsonb +); + +COMMIT; \ No newline at end of file From fb3c6a739a181a7f3fd98fff5cd5a0990c79fe27 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 22 Mar 2022 17:51:25 +0100 Subject: [PATCH 12/87] feat(api): dashboards insights --- api/app.py | 4 ++-- api/chalicelib/core/insights.py | 7 ++----- ee/api/app.py | 4 ++-- ee/api/chalicelib/core/insights.py | 32 +++++++++++++++--------------- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/api/app.py b/api/app.py index d261dadac..4d3697e92 100644 --- a/api/app.py +++ b/api/app.py @@ -12,7 +12,7 @@ from routers import core, core_dynamic from routers.app import v1_api from routers.crons import core_crons from routers.crons import core_dynamic_crons -from routers.subs import dashboard +from routers.subs import dashboard, insights app = FastAPI() @@ -54,7 +54,7 @@ app.include_router(core_dynamic.public_app) app.include_router(core_dynamic.app) app.include_router(core_dynamic.app_apikey) app.include_router(dashboard.app) -# app.include_router(insights.app) +app.include_router(insights.app) app.include_router(v1_api.app_apikey) Schedule = AsyncIOScheduler() diff --git a/api/chalicelib/core/insights.py b/api/chalicelib/core/insights.py index 08adfd3ca..5b3894606 100644 --- a/api/chalicelib/core/insights.py +++ b/api/chalicelib/core/insights.py @@ -1,11 +1,8 @@ import schemas -from chalicelib.core import sessions_metas +from chalicelib.core.dashboard import __get_constraints, __get_constraint_values from chalicelib.utils import helper, dev from chalicelib.utils import pg_client from chalicelib.utils.TimeUTC import TimeUTC -from chalicelib.utils.metrics_helper import __get_step_size -import math -from chalicelib.core.dashboard import __get_constraints, __get_constraint_values def __transform_journey(rows): @@ -930,4 +927,4 @@ def search(text, feature_type, project_id, platform=None): rows = cur.fetchall() else: return [] - return [helper.dict_to_camel_case(row) for row in rows] \ No newline at end of file + return [helper.dict_to_camel_case(row) for row in rows] diff --git a/ee/api/app.py b/ee/api/app.py index fdf7f60b8..deae9d78d 100644 --- a/ee/api/app.py +++ b/ee/api/app.py @@ -14,7 +14,7 @@ from routers import core, core_dynamic, ee, saml from routers.app import v1_api, v1_api_ee from routers.crons import core_crons from routers.crons import core_dynamic_crons -from routers.subs import dashboard +from routers.subs import dashboard, insights app = FastAPI() @@ -65,7 +65,7 @@ app.include_router(saml.public_app) app.include_router(saml.app) app.include_router(saml.app_apikey) app.include_router(dashboard.app) -# app.include_router(insights.app) +app.include_router(insights.app) app.include_router(v1_api.app_apikey) app.include_router(v1_api_ee.app_apikey) diff --git a/ee/api/chalicelib/core/insights.py b/ee/api/chalicelib/core/insights.py index 387029fd4..ff9d4dad4 100644 --- a/ee/api/chalicelib/core/insights.py +++ b/ee/api/chalicelib/core/insights.py @@ -1,9 +1,9 @@ -from chalicelib.core import sessions_metas -from chalicelib.utils import helper, dev -from chalicelib.utils import ch_client -from chalicelib.utils.TimeUTC import TimeUTC -from chalicelib.core.dashboard import __get_constraint_values, __complete_missing_steps +import schemas from chalicelib.core.dashboard import __get_basic_constraints, __get_meta_constraint +from chalicelib.core.dashboard import __get_constraint_values, __complete_missing_steps +from chalicelib.utils import ch_client +from chalicelib.utils import helper, dev +from chalicelib.utils.TimeUTC import TimeUTC def __transform_journey(rows): @@ -42,7 +42,7 @@ def journey(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp= elif f["type"] == "EVENT_TYPE" and JOURNEY_TYPES.get(f["value"]): event_table = JOURNEY_TYPES[f["value"]]["table"] event_column = JOURNEY_TYPES[f["value"]]["column"] - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append(f"sessions_metadata.project_id = %(project_id)s") meta_condition.append(f"sessions_metadata.datetime >= toDateTime(%(startTimestamp)s / 1000)") @@ -303,7 +303,7 @@ def feature_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), en elif f["type"] == "EVENT_VALUE": event_value = f["value"] default = False - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append("sessions_metadata.user_id IS NOT NULL") meta_condition.append("not empty(sessions_metadata.user_id)") @@ -404,7 +404,7 @@ def feature_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), elif f["type"] == "EVENT_VALUE": event_value = f["value"] default = False - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append("sessions_metadata.user_id IS NOT NULL") meta_condition.append("not empty(sessions_metadata.user_id)") @@ -512,7 +512,7 @@ def feature_popularity_frequency(project_id, startTimestamp=TimeUTC.now(delta_da if f["type"] == "EVENT_TYPE" and JOURNEY_TYPES.get(f["value"]): event_table = JOURNEY_TYPES[f["value"]]["table"] event_column = JOURNEY_TYPES[f["value"]]["column"] - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append("sessions_metadata.user_id IS NOT NULL") meta_condition.append("not empty(sessions_metadata.user_id)") @@ -586,7 +586,7 @@ def feature_adoption(project_id, startTimestamp=TimeUTC.now(delta_days=-70), end elif f["type"] == "EVENT_VALUE": event_value = f["value"] default = False - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append("sessions_metadata.user_id IS NOT NULL") meta_condition.append("not empty(sessions_metadata.user_id)") @@ -672,7 +672,7 @@ def feature_adoption_top_users(project_id, startTimestamp=TimeUTC.now(delta_days elif f["type"] == "EVENT_VALUE": event_value = f["value"] default = False - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append("user_id IS NOT NULL") meta_condition.append("not empty(sessions_metadata.user_id)") @@ -742,7 +742,7 @@ def feature_adoption_daily_usage(project_id, startTimestamp=TimeUTC.now(delta_da elif f["type"] == "EVENT_VALUE": event_value = f["value"] default = False - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append("sessions_metadata.project_id = %(project_id)s") meta_condition.append("sessions_metadata.datetime >= toDateTime(%(startTimestamp)s/1000)") @@ -807,7 +807,7 @@ def feature_intensity(project_id, startTimestamp=TimeUTC.now(delta_days=-70), en if f["type"] == "EVENT_TYPE" and JOURNEY_TYPES.get(f["value"]): event_table = JOURNEY_TYPES[f["value"]]["table"] event_column = JOURNEY_TYPES[f["value"]]["column"] - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append("sessions_metadata.project_id = %(project_id)s") meta_condition.append("sessions_metadata.datetime >= toDateTime(%(startTimestamp)s/1000)") @@ -847,7 +847,7 @@ def users_active(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTime for f in filters: if f["type"] == "PERIOD" and f["value"] in ["DAY", "WEEK"]: period = f["value"] - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") extra_values["user_id"] = f["value"] period_function = PERIOD_TO_FUNCTION[period] @@ -940,7 +940,7 @@ def users_slipping(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTi elif f["type"] == "EVENT_VALUE": event_value = f["value"] default = False - elif f["type"] in [sessions_metas.meta_type.USERID, sessions_metas.meta_type.USERID_IOS]: + elif f["type"] in [schemas.FilterType.user_id, schemas.FilterType.user_id_ios]: meta_condition.append(f"sessions_metadata.user_id = %(user_id)s") meta_condition.append("sessions_metadata.project_id = %(project_id)s") meta_condition.append("sessions_metadata.datetime >= toDateTime(%(startTimestamp)s/1000)") @@ -1044,4 +1044,4 @@ def search(text, feature_type, project_id, platform=None): rows = ch.execute(ch_query, params) else: return [] - return [helper.dict_to_camel_case(row) for row in rows] \ No newline at end of file + return [helper.dict_to_camel_case(row) for row in rows] From da336d01407b7d4ce9d03db54e1466ceff59b139 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 24 Mar 2022 09:24:48 +0100 Subject: [PATCH 13/87] feat(ui) - dashboards wip --- frontend/app/Router.js | 14 +- .../app/components/Dashboard/NewDashboard.tsx | 89 ++++++---- .../Dashboard/WidgetView/WidgetView.tsx | 20 --- .../DashboardSideMenu/DashboardSideMenu.tsx | 11 +- .../components/FilterSeries/FilterSeries.tsx | 111 ++++++++++++ .../FilterSeries/SeriesName/SeriesName.tsx | 57 +++++++ .../FilterSeries/SeriesName/index.ts | 1 + .../components/FilterSeries/index.ts | 1 + .../components/MetricsView/MetricsView.tsx | 16 ++ .../Dashboard/components/MetricsView/index.ts | 1 + .../components/WidgetForm/WidgetForm.tsx | 160 ++++++++++++++++++ .../Dashboard/components/WidgetForm/index.ts | 1 + .../WidgetPreview/WidgetPreview.tsx | 87 ++++++++++ .../components/WidgetPreview/index.ts | 1 + .../WidgetSessions/WidgetSessions.tsx | 37 ++++ .../components/WidgetSessions/index.ts | 1 + .../components/WidgetView/WidgetView.tsx | 41 +++++ .../{ => components}/WidgetView/index.ts | 0 .../Dashboard/store/dashboardStore.ts | 12 +- .../app/components/Dashboard/store/filter.ts | 37 ++++ .../Dashboard/store/filterSeries.ts | 22 +++ .../app/components/Dashboard/store/widget.ts | 35 +++- .../FilterSeries/FilterSeries.tsx | 16 +- .../shared/DropdownPlain/DropdownPlain.css | 2 +- .../shared/Filters/FilterList/FilterList.tsx | 16 +- .../ui/DropdownPlain/DropdownPlain.js | 1 - frontend/app/routes.js | 12 +- frontend/app/svg/icons/bar-chart-line.svg | 2 +- .../app/svg/icons/chevron-double-left.svg | 2 +- .../app/svg/icons/chevron-double-right.svg | 2 +- frontend/app/svg/icons/columns-gap.svg | 3 + frontend/app/svg/icons/controller.svg | 2 +- frontend/app/svg/icons/flag-na.svg | 2 +- frontend/tailwind.config.js | 2 +- 34 files changed, 730 insertions(+), 87 deletions(-) delete mode 100644 frontend/app/components/Dashboard/WidgetView/WidgetView.tsx create mode 100644 frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx create mode 100644 frontend/app/components/Dashboard/components/FilterSeries/SeriesName/SeriesName.tsx create mode 100644 frontend/app/components/Dashboard/components/FilterSeries/SeriesName/index.ts create mode 100644 frontend/app/components/Dashboard/components/FilterSeries/index.ts create mode 100644 frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx create mode 100644 frontend/app/components/Dashboard/components/MetricsView/index.ts create mode 100644 frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx create mode 100644 frontend/app/components/Dashboard/components/WidgetForm/index.ts create mode 100644 frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx create mode 100644 frontend/app/components/Dashboard/components/WidgetPreview/index.ts create mode 100644 frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx create mode 100644 frontend/app/components/Dashboard/components/WidgetSessions/index.ts create mode 100644 frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx rename frontend/app/components/Dashboard/{ => components}/WidgetView/index.ts (100%) create mode 100644 frontend/app/components/Dashboard/store/filter.ts create mode 100644 frontend/app/components/Dashboard/store/filterSeries.ts create mode 100644 frontend/app/svg/icons/columns-gap.svg diff --git a/frontend/app/Router.js b/frontend/app/Router.js index a6a9ba090..74e8845f7 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -15,7 +15,7 @@ import LiveSessionPure from 'Components/Session/LiveSession'; import AssistPure from 'Components/Assist'; import BugFinderPure from 'Components/BugFinder/BugFinder'; import DashboardPure from 'Components/Dashboard/NewDashboard'; -import WidgetViewPure from 'Components/Dashboard/WidgetView'; +import WidgetViewPure from 'Components/Dashboard/components/WidgetView'; import ErrorsPure from 'Components/Errors/Errors'; import Header from 'Components/Header/Header'; // import ResultsModal from 'Shared/Results/ResultsModal'; @@ -49,10 +49,8 @@ const withSiteId = routes.withSiteId; const withObTab = routes.withObTab; const DASHBOARD_PATH = routes.dashboard(); -// const DASHBOARD_WIDGET_CREATE_PATH = routes.dashboardMetricCreate(); -// const DASHBOARD_WIDGET_DETAILS_PATH = routes.dashboardMetricDetails(); -// const METRIC_CREATE_PATH = routes.metricCreate(); -// const METRIC_DETAILS_PATH = routes.metricDetails(); +const DASHBOARD_SELECT_PATH = routes.dashboardSelected(); +const DASHBOARD_METRICS_PATH = routes.dashboardMetrics(); // const WIDGET_PATAH = routes.dashboardMetric(); const SESSIONS_PATH = routes.sessions(); @@ -187,7 +185,11 @@ class Router extends React.Component { { siteIdList.length === 0 && } - + + + {/* */} + {/* */} + {/* diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 831e44bd8..77a784ec2 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -5,12 +5,19 @@ import { observer } from "mobx-react-lite"; import { useDashboardStore } from './store/store'; import { withRouter } from 'react-router-dom'; import DashboardView from './components/DashboardView'; -import { dashboardSelected, dashboardMetricDetails, dashboardMetricCreate, withSiteId } from 'App/routes'; +import { + dashboardSelected, + dashboardMetricDetails, + dashboardMetricCreate, + withSiteId, + dashboardMetrics, +} from 'App/routes'; import DashboardSideMenu from './components/DashboardSideMenu'; -import WidgetView from './WidgetView'; +import WidgetView from './components/WidgetView'; +import MetricsView from './components/MetricsView'; function NewDashboard(props) { - const { match: { params: { siteId, dashboardId, metricId } } } = props; + const { history, match: { params: { siteId, dashboardId, metricId } } } = props; const store: any = useDashboardStore(); const dashboard = store.selectedDashboard; @@ -19,42 +26,66 @@ function NewDashboard(props) { }, []); useEffect(() => { - console.log('dashboardId', dashboardId); if (!dashboard || !dashboard.dashboardId) { if (dashboardId) { store.selectDashboardById(dashboardId); } else { store.selectDefaultDashboard(); } - } + } + + // if (dashboard) { + // if (dashboard.dashboardId !== dashboardId) { + // history.push(withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId))); + // } + + // history.replace(withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId))); + // } + // console.log('dashboard', dashboard) }, [dashboard]); + console.log('rendering dashboard', props.match.params); + return ( -
-
- -
-
- { dashboard && dashboard.dashboardId && ( - - - - - - - - - - - {/* - - */} - - - )} -
-
+ <> + {/* { dashboard && dashboard.dashboardId && ( */} + + +
+
+ +
+
+ +
+
+
+ { dashboardId && ( + <> + +
+
+ +
+
+ +
+
+
+ + {/* + + + + + */} + {/* */} + + )} +
+ {/* )} */} + ); } diff --git a/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx deleted file mode 100644 index cfa34cd5c..000000000 --- a/frontend/app/components/Dashboard/WidgetView/WidgetView.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { withRouter } from 'react-router-dom'; -import { useDashboardStore } from '../store/store'; - -function WidgetView(props) { - console.log('WidgetView', props); - const store: any = useDashboardStore(); - const widget = store.currentWidget; - return ( -
-
-
-

{widget.name}

-
-
-
- ); -} - -export default withRouter(WidgetView); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 4c984904e..7f223bb55 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -3,17 +3,20 @@ import React from 'react'; import { SideMenuitem, SideMenuHeader, Icon } from 'UI'; import { withDashboardStore } from '../../store/store'; import { withRouter } from 'react-router-dom'; -import { withSiteId, dashboardSelected } from 'App/routes'; +import { withSiteId, dashboardSelected, dashboardMetrics } from 'App/routes'; function DashboardSideMenu(props) { const { store, history } = props; const { dashboardId } = store.selectedDashboard; + const redirect = (path) => { + history.push(path); + } + const onItemClick = (dashboard) => { store.selectDashboardById(dashboard.dashboardId); const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(store.siteId)); - // console.log('path', path); - // history.push(path); + history.push(path); }; return ( @@ -41,7 +44,7 @@ function DashboardSideMenu(props) { id="menu-manage-alerts" title="Metrics" iconName="bar-chart-line" - // onClick={() => setShowAlerts(true)} + onClick={() => redirect(withSiteId(dashboardMetrics(), store.siteId))} />
diff --git a/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx new file mode 100644 index 000000000..c43e1b97a --- /dev/null +++ b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx @@ -0,0 +1,111 @@ +import React, { useState } from 'react'; +import FilterList from 'Shared/Filters/FilterList'; +import { + edit, + updateSeries, + addSeriesFilterFilter, + removeSeriesFilterFilter, + editSeriesFilterFilter, + editSeriesFilter, +} from 'Duck/customMetrics'; +import { connect } from 'react-redux'; +import { IconButton, Icon } from 'UI'; +import FilterSelection from 'Shared/Filters/FilterSelection'; +import SeriesName from './SeriesName'; +import cn from 'classnames'; +import { useDashboardStore } from '../../store/store'; +import { observer, useObserver } from 'mobx-react-lite'; + +interface Props { + seriesIndex: number; + series: any; + edit: typeof edit; + updateSeries: typeof updateSeries; + onRemoveSeries: (seriesIndex) => void; + canDelete?: boolean; + addSeriesFilterFilter: typeof addSeriesFilterFilter; + editSeriesFilterFilter: typeof editSeriesFilterFilter; + editSeriesFilter: typeof editSeriesFilter; + removeSeriesFilterFilter: typeof removeSeriesFilterFilter; + hideHeader?: boolean; + emptyMessage?: any; +} + +function FilterSeries(props: Props) { + const { canDelete, hideHeader = false, emptyMessage = 'Add user event or filter to define the series by clicking Add Step.' } = props; + const [expanded, setExpanded] = useState(true) + const { series, seriesIndex } = props; + + const onAddFilter = (filter) => { + series.filter.addFilter(filter) + } + + const onUpdateFilter = (filterIndex, filter) => { + series.filter.updateFilter(filterIndex, filter) + } + + const onChangeEventsOrder = (e, { name, value }) => { + series.filter.updateKey(name, value) + // props.editSeriesFilter(seriesIndex, { eventsOrder: value }); + } + + const onRemoveFilter = (filterIndex) => { + series.filter.removeFilter(filterIndex) + // props.removeSeriesFilterFilter(seriesIndex, filterIndex); + } + + return ( +
+
+
+ props.updateSeries(seriesIndex, { name }) } /> +
+ +
+
+ +
+ +
setExpanded(!expanded)} className="ml-3"> + +
+
+
+ { expanded && ( + <> +
+ { series.filter.filters.length > 0 ? ( + + ): ( +
{emptyMessage}
+ )} +
+
+
+ + + +
+
+ + )} +
+ ); +} + +export default connect(null, { + edit, + updateSeries, + addSeriesFilterFilter, + editSeriesFilterFilter, + editSeriesFilter, + removeSeriesFilterFilter, +})(observer(FilterSeries)); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/SeriesName.tsx b/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/SeriesName.tsx new file mode 100644 index 000000000..5d25e9de9 --- /dev/null +++ b/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/SeriesName.tsx @@ -0,0 +1,57 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Icon } from 'UI'; + +interface Props { + name: string; + onUpdate: (name) => void; + seriesIndex?: number; +} +function SeriesName(props: Props) { + const { seriesIndex = 1 } = props; + const [editing, setEditing] = useState(false) + const [name, setName] = useState(props.name) + const ref = useRef(null) + + const write = ({ target: { value, name } }) => { + setName(value) + } + + const onBlur = () => { + setEditing(false) + props.onUpdate(name) + } + + useEffect(() => { + if (editing) { + ref.current.focus() + } + }, [editing]) + + useEffect(() => { + setName(props.name) + }, [props.name]) + + // const { name } = props; + return ( +
+ { editing ? ( + setEditing(true)} + /> + ) : ( +
{name.trim() === '' ? 'Seriess ' + (seriesIndex + 1) : name }
+ )} + +
setEditing(true)}>
+
+ ); +} + +export default SeriesName; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/index.ts b/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/index.ts new file mode 100644 index 000000000..90e63cdb6 --- /dev/null +++ b/frontend/app/components/Dashboard/components/FilterSeries/SeriesName/index.ts @@ -0,0 +1 @@ +export { default } from './SeriesName'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/FilterSeries/index.ts b/frontend/app/components/Dashboard/components/FilterSeries/index.ts new file mode 100644 index 000000000..5882e382a --- /dev/null +++ b/frontend/app/components/Dashboard/components/FilterSeries/index.ts @@ -0,0 +1 @@ +export { default } from './FilterSeries' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx new file mode 100644 index 000000000..d358075db --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Button, PageTitle, Link } from 'UI'; +import { withSiteId, dashboardMetricCreate } from 'App/routes'; + +function MetricsView(props) { + return ( +
+
+ + {/* */} +
+
+ ); +} + +export default MetricsView; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsView/index.ts b/frontend/app/components/Dashboard/components/MetricsView/index.ts new file mode 100644 index 000000000..bfebac6b9 --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricsView/index.ts @@ -0,0 +1 @@ +export { default } from './MetricsView'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx new file mode 100644 index 000000000..631a3b791 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -0,0 +1,160 @@ +import React from 'react'; +import DropdownPlain from 'Shared/DropdownPlain'; +import { metricTypes, metricOf, issueOptions } from 'App/constants/filterOptions'; +import { FilterKey } from 'Types/filter/filterType'; +import { useDashboardStore } from '../../store/store'; +import { useObserver } from 'mobx-react-lite'; +import { HelpText, Button, Icon } from 'UI' +import FilterSeries from '../FilterSeries'; + +interface Props { + // metric: any, + // editWidget: (metric, shouldFetch?) => void +} + +function WidgetForm(props: Props) { + // const { metric } = props; + const store: any = useDashboardStore(); + const metric = store.currentWidget; + + const timeseriesOptions = metricOf.filter(i => i.type === 'timeseries'); + const tableOptions = metricOf.filter(i => i.type === 'table'); + const isTable = metric.metricType === 'table'; + const isTimeSeries = metric.metricType === 'timeseries'; + const _issueOptions = [{ text: 'All', value: 'all' }].concat(issueOptions); + + const write = ({ target: { value, name } }) => store.editWidget({ [ name ]: value }, false); + const writeOption = (e, { value, name }) => { + store.editWidget({ [ name ]: value }, false); + + if (name === 'metricValue') { + store.editWidget({ metricValue: [value] }, false); + } + + if (name === 'metricOf') { + if (value === FilterKey.ISSUE) { + store.editWidget({ metricValue: ['all'] }, false); + } + } + + if (name === 'metricType') { + if (value === 'timeseries') { + store.editWidget({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }, false); + } else if (value === 'table') { + store.editWidget({ metricOf: tableOptions[0].value, viewType: 'table' }, false); + } + } + }; + + return useObserver(() => ( +
+
+ +
+ + + {metric.metricType === 'timeseries' && ( + <> + of + + + )} + + {metric.metricType === 'table' && ( + <> + of + + + )} + + {metric.metricOf === FilterKey.ISSUE && ( + <> + issue type + + + )} + + {metric.metricType === 'table' && ( + <> + showing + + + )} +
+
+ +
+ + + {metric.series.length > 0 && metric.series.slice(0, isTable ? 1 : metric.series.length).map((series: any, index: number) => ( +
+ removeSeries(index)} + onRemoveSeries={() => metric.removeSeries(index)} + canDelete={metric.series.length > 1} + emptyMessage={isTable ? + 'Filter data using any event or attribute. Use Add Step button below to do so.' : + 'Add user event or filter to define the series by clicking Add Step.' + } + /> +
+ ))} +
+ +
+ +
+ + +
+
+
+ )); +} + +export default WidgetForm; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetForm/index.ts b/frontend/app/components/Dashboard/components/WidgetForm/index.ts new file mode 100644 index 000000000..283f9ec23 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetForm/index.ts @@ -0,0 +1 @@ +export { default } from './WidgetForm'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx new file mode 100644 index 000000000..4e6de9662 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import cn from 'classnames'; +import WidgetWrapper from '../../WidgetWrapper'; +import { useDashboardStore } from '../../store/store'; +import { Loader, NoContent, SegmentSelection, Icon } from 'UI'; +import DateRange from 'Shared/DateRange'; +import { useObserver } from 'mobx-react-lite'; + +interface Props { + className?: string; +} +function WidgetPreview(props: Props) { + const { className = '' } = props; + const store: any = useDashboardStore(); + const metric = store.currentWidget; + const isTimeSeries = metric.metricType === 'timeseries'; + const isTable = metric.metricType === 'table'; + + const chagneViewType = (e, { name, value }) => { + metric.update({ [ name ]: value }); + } + + const onDateChange = (changedDates) => { + // setPeriod({ ...changedDates, rangeName: changedDates.rangeValue }) + metric.update({ ...changedDates, rangeName: changedDates.rangeValue }); + } + + return useObserver(() => ( +
+
+

Trend

+
+ {isTimeSeries && ( + <> + Visualization + + + )} + + {isTable && ( + <> + Visualization + + + )} +
+ Time Range + +
+
+
+ +
+
+ )); +} + +export default WidgetPreview; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/index.ts b/frontend/app/components/Dashboard/components/WidgetPreview/index.ts new file mode 100644 index 000000000..9d28f8146 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetPreview/index.ts @@ -0,0 +1 @@ +export { default } from './WidgetPreview'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx new file mode 100644 index 000000000..60eb1a569 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import cn from 'classnames'; +import { useDashboardStore } from '../../store/store'; +import SessionItem from 'Shared/SessionItem'; + +interface Props { + className?: string; +} +function WidgetSessions(props: Props) { + const { className = '' } = props; + const store: any = useDashboardStore(); + const widget = store.currentWidget; + + return ( +
+
+

Sessions

+ {/*
Showing all sessions between {startTime} and {endTime}
*/} +
+ +
+ + {widget.sessions.map((session: any) => ( + + ))} + +
+
+ ); +} + +export default WidgetSessions; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/index.ts b/frontend/app/components/Dashboard/components/WidgetSessions/index.ts new file mode 100644 index 000000000..8f309114a --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetSessions/index.ts @@ -0,0 +1 @@ +export { default } from './WidgetSessions'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx new file mode 100644 index 000000000..cbe2d38e4 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -0,0 +1,41 @@ +import React, { useState } from 'react'; +import { withRouter } from 'react-router-dom'; +import { useDashboardStore } from '../../store/store'; +import WidgetForm from '../WidgetForm'; +import WidgetPreview from '../WidgetPreview'; +import WidgetSessions from '../WidgetSessions'; +import { Icon } from 'UI'; + +interface Props { + +} +function WidgetView(props: Props) { + const [expanded, setExpanded] = useState(true); + const store: any = useDashboardStore(); + const widget = store.currentWidget; + return ( +
+
+
+

{widget.name}

+
+
setExpanded(!expanded)} + className="flex items-center cursor-pointer select-none" + > + {expanded ? 'Collapse' : 'Expand'} + +
+
+
+ + { expanded && } +
+ + + +
+ ); +} + +export default withRouter(WidgetView); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/WidgetView/index.ts b/frontend/app/components/Dashboard/components/WidgetView/index.ts similarity index 100% rename from frontend/app/components/Dashboard/WidgetView/index.ts rename to frontend/app/components/Dashboard/components/WidgetView/index.ts diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 832988ce9..8f03c4e59 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -18,6 +18,7 @@ export default class DashboardStore { selectedDashboard: observable, isLoading: observable, + resetCurrentWidget: action, addDashboard: action, removeDashboard: action, updateDashboard: action, @@ -31,6 +32,7 @@ export default class DashboardStore { toJson: action, fromJson: action, setSiteId: action, + editWidget: action, }) @@ -48,6 +50,14 @@ export default class DashboardStore { // }, 3000) } + resetCurrentWidget() { + this.currentWidget = new Widget() + } + + editWidget(widget: Widget) { + this.currentWidget.update(widget) + } + fetchList() { this.isLoading = true @@ -210,7 +220,7 @@ function getRandomWidget() { const widget = new Widget(); widget.widgetId = Math.floor(Math.random() * 100); widget.name = randomMetricName(); - widget.type = "random"; + // widget.type = "random"; widget.colSpan = Math.floor(Math.random() * 2) + 1; return widget; } diff --git a/frontend/app/components/Dashboard/store/filter.ts b/frontend/app/components/Dashboard/store/filter.ts new file mode 100644 index 000000000..c9bdd3452 --- /dev/null +++ b/frontend/app/components/Dashboard/store/filter.ts @@ -0,0 +1,37 @@ +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" + +export default class Filter { + name: string = '' + filters: any[] = [] + eventsOrder: string = 'then' + + constructor() { + makeAutoObservable(this, { + addFilter: action, + removeFilter: action, + updateKey: action, + }) + } + + addFilter(filter: any) { + filter.value = [""] + if (filter.hasOwnProperty('filters')) { + filter.filters = filter.filters.map(i => ({ ...i, value: [""] })) + } + this.filters.push(filter) + console.log('addFilter', this.filters) + } + + updateFilter(index: number, filter: any) { + this.filters[index] = filter + console.log('updateFilter', this.filters) + } + + updateKey(key, value) { + this[key] = value + } + + removeFilter(index: number) { + this.filters.splice(index, 1) + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/filterSeries.ts b/frontend/app/components/Dashboard/store/filterSeries.ts new file mode 100644 index 000000000..284e50230 --- /dev/null +++ b/frontend/app/components/Dashboard/store/filterSeries.ts @@ -0,0 +1,22 @@ +// import Filter from 'Types/filter'; +import Filter from './filter' +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" + +export default class FilterSeries { + seriesId?: any = undefined + name: string = "Series 1" + filter: Filter = new Filter() + + constructor() { + makeAutoObservable(this, { + name: observable, + filter: observable, + + update: action, + }) + } + + update(key, value) { + this[key] = value + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/widget.ts b/frontend/app/components/Dashboard/store/widget.ts index 2cd1bee32..27f5dfd46 100644 --- a/frontend/app/components/Dashboard/store/widget.ts +++ b/frontend/app/components/Dashboard/store/widget.ts @@ -1,9 +1,17 @@ import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import Filter from 'Types/filter'; +import FilterSeries from "./filterSeries"; export default class Widget { widgetId: any = undefined name: string = "New Metric" - type: string = "" + metricType: string = "timeseries" + metricOf: string = "sessionCount" + metricValue: string = "" + viewType: string = "lineChart" + series: FilterSeries[] = [] + sessions: [] = [] + position: number = 0 data: any = {} isLoading: boolean = false @@ -15,26 +23,46 @@ export default class Widget { makeAutoObservable(this, { widgetId: observable, name: observable, - type: observable, + metricType: observable, + metricOf: observable, position: observable, data: observable, isLoading: observable, isValid: observable, dashboardId: observable, + addSeries: action, colSpan: observable, fromJson: action, toJson: action, validate: action, update: action, + udpateKey: action, }) + + const filterSeries = new FilterSeries() + this.series.push(filterSeries) } + udpateKey(key: string, value: any) { + this[key] = value + } + + removeSeries(index: number) { + this.series.splice(index, 1) + } + + addSeries() { + const series = new FilterSeries() + series.name = "Series " + (this.series.length + 1) + this.series.push(series) + } + + fromJson(json: any) { runInAction(() => { this.widgetId = json.widgetId this.name = json.name - this.type = json.type this.data = json.data }) return this @@ -44,7 +72,6 @@ export default class Widget { return { widgetId: this.widgetId, name: this.name, - type: this.type, data: this.data } } diff --git a/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx b/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx index 5327308dd..c86f626ac 100644 --- a/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx +++ b/frontend/app/components/shared/CustomMetrics/FilterSeries/FilterSeries.tsx @@ -86,13 +86,15 @@ function FilterSeries(props: Props) {
{emptyMessage}
)}
-
- - - +
+
+ + + +
)} diff --git a/frontend/app/components/shared/DropdownPlain/DropdownPlain.css b/frontend/app/components/shared/DropdownPlain/DropdownPlain.css index dd7b9a2a5..772060e87 100644 --- a/frontend/app/components/shared/DropdownPlain/DropdownPlain.css +++ b/frontend/app/components/shared/DropdownPlain/DropdownPlain.css @@ -1,6 +1,6 @@ .dropdown { display: flex !important; - padding: 4px; + padding: 4px 8px; border-radius: 3px; color: $gray-darkest; font-weight: 500; diff --git a/frontend/app/components/shared/Filters/FilterList/FilterList.tsx b/frontend/app/components/shared/Filters/FilterList/FilterList.tsx index 8eba891bb..110702ac7 100644 --- a/frontend/app/components/shared/Filters/FilterList/FilterList.tsx +++ b/frontend/app/components/shared/Filters/FilterList/FilterList.tsx @@ -1,6 +1,8 @@ import React, { useState} from 'react'; import FilterItem from '../FilterItem'; import { SegmentSelection, Popup } from 'UI'; +import { List } from 'immutable'; +import { useObserver } from 'mobx-react-lite'; interface Props { // filters: any[]; // event/filter @@ -12,16 +14,16 @@ interface Props { } function FilterList(props: Props) { const { filter, hideEventsOrder = false } = props; - const filters = filter.filters; - const hasEvents = filter.filters.filter(i => i.isEvent).size > 0; - const hasFilters = filter.filters.filter(i => !i.isEvent).size > 0; + const filters = List(filter.filters); + const hasEvents = filters.filter((i: any) => i.isEvent).size > 0; + const hasFilters = filters.filter((i: any) => !i.isEvent).size > 0; let rowIndex = 0; const onRemoveFilter = (filterIndex) => { props.onRemoveFilter(filterIndex); } - return ( + return useObserver(() => (
{ hasEvents && ( <> @@ -54,7 +56,7 @@ function FilterList(props: Props) {
)}
- {filters.map((filter, filterIndex) => filter.isEvent ? ( + {filters.map((filter: any, filterIndex: any) => filter.isEvent ? ( {hasEvents &&
}
FILTERS
- {filters.map((filter, filterIndex) => !filter.isEvent ? ( + {filters.map((filter: any, filterIndex: any) => !filter.isEvent ? ( )}
- ); + )); } export default FilterList; \ No newline at end of file diff --git a/frontend/app/components/ui/DropdownPlain/DropdownPlain.js b/frontend/app/components/ui/DropdownPlain/DropdownPlain.js index 389b75b93..ce1fc506d 100644 --- a/frontend/app/components/ui/DropdownPlain/DropdownPlain.js +++ b/frontend/app/components/ui/DropdownPlain/DropdownPlain.js @@ -21,7 +21,6 @@ function DropdownPlain({ name, label, options, onChange, defaultValue, wrapperSt options={ options } onChange={ onChange } defaultValue={ defaultValue || options[ 0 ].value } - icon={null} disabled={disabled} icon={ } /> diff --git a/frontend/app/routes.js b/frontend/app/routes.js index 8693ae3e6..4aea022a4 100644 --- a/frontend/app/routes.js +++ b/frontend/app/routes.js @@ -101,6 +101,7 @@ export const testBuilderNew = () => '/test-builder'; export const testBuilder = (testId = ':testId') => `/test-builder/${ testId }`; export const dashboard = () => '/dashboard'; +export const dashboardMetrics = () => '/dashboard/metrics'; export const dashboardSelected = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }`, hash); export const dashboardMetricDetails = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash); @@ -121,6 +122,7 @@ const REQUIRED_SITE_ID_ROUTES = [ assist(), dashboard(''), dashboardSelected(''), + dashboardMetrics(''), // dashboardMetricCreate(''), dashboardMetricDetails(''), metricCreate(''), @@ -152,7 +154,15 @@ export function isRoute(route, path){ routeParts.every((p, i) => p.startsWith(':') || p === pathParts[ i ]); } -const SITE_CHANGE_AVALIABLE_ROUTES = [ sessions(), assist(), dashboard(), dashboardSelected(''), errors(), onboarding('')]; +const SITE_CHANGE_AVALIABLE_ROUTES = [ + sessions(), + assist(), + dashboard(), + dashboardMetrics(''), + dashboardSelected(''), + errors(), + onboarding('') +]; export const siteChangeAvaliable = path => SITE_CHANGE_AVALIABLE_ROUTES.some(r => isRoute(r, path)); export const redirects = Object.entries({ diff --git a/frontend/app/svg/icons/bar-chart-line.svg b/frontend/app/svg/icons/bar-chart-line.svg index e3f0cf255..c46c1d8f8 100644 --- a/frontend/app/svg/icons/bar-chart-line.svg +++ b/frontend/app/svg/icons/bar-chart-line.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/frontend/app/svg/icons/chevron-double-left.svg b/frontend/app/svg/icons/chevron-double-left.svg index 7181fd111..8694e8195 100644 --- a/frontend/app/svg/icons/chevron-double-left.svg +++ b/frontend/app/svg/icons/chevron-double-left.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/frontend/app/svg/icons/chevron-double-right.svg b/frontend/app/svg/icons/chevron-double-right.svg index 73e1b352d..ee4323e06 100644 --- a/frontend/app/svg/icons/chevron-double-right.svg +++ b/frontend/app/svg/icons/chevron-double-right.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/frontend/app/svg/icons/columns-gap.svg b/frontend/app/svg/icons/columns-gap.svg new file mode 100644 index 000000000..f4ed1f9ae --- /dev/null +++ b/frontend/app/svg/icons/columns-gap.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/app/svg/icons/controller.svg b/frontend/app/svg/icons/controller.svg index 15e777456..7c9499541 100644 --- a/frontend/app/svg/icons/controller.svg +++ b/frontend/app/svg/icons/controller.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/frontend/app/svg/icons/flag-na.svg b/frontend/app/svg/icons/flag-na.svg index ca42ac405..33796e2f0 100644 --- a/frontend/app/svg/icons/flag-na.svg +++ b/frontend/app/svg/icons/flag-na.svg @@ -1,3 +1,3 @@ - + diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 0da29c3d5..28fd4d982 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -93,7 +93,7 @@ module.exports = { // 'transitionProperty', // 'transitionTimingFunction', // 'translate', - // 'userSelect', + 'userSelect', // 'verticalAlign', 'visibility', 'whitespace', From d095ec5e0d68ca24a1be88b61914b9f7fd415abd Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 24 Mar 2022 11:04:48 +0100 Subject: [PATCH 14/87] feat(ui) - dashboards wip --- .../components/MetricsList/MetricsList.tsx | 55 +++++++++++++++++++ .../Dashboard/components/MetricsList/index.ts | 1 + .../components/MetricsView/MetricsView.tsx | 12 +++- .../Dashboard/store/dashboardStore.ts | 12 ++++ .../app/components/Dashboard/store/widget.ts | 4 ++ frontend/app/svg/icons/person-fill.svg | 3 + 6 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx create mode 100644 frontend/app/components/Dashboard/components/MetricsList/index.ts create mode 100644 frontend/app/svg/icons/person-fill.svg diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx new file mode 100644 index 000000000..5f7c19b17 --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -0,0 +1,55 @@ +import { useObserver } from 'mobx-react-lite'; +import React from 'react'; +import { Icon, NoContent, Label, Link } from 'UI'; +import { useDashboardStore } from '../../store/store'; + +interface Props { } +function MetricsList(props: Props) { + const store: any = useDashboardStore(); + const widgets = store.widgets; + const lenth = widgets.length; + + return useObserver(() => ( + +
+
+
Title
+
Type
+
Dashboards
+
Owner
+
Visibility & Edit Access
+
Last Modified
+
+ + {widgets.map((metric: any) => ( +
+
+ + {metric.name} + +
+
+
Dashboards
+
{metric.owner}
+
+ {metric.isPrivate ? ( +
+ + Private +
+ ) : ( +
+ + Team +
+ )} +
+
Last Modified
+
+ ))} +
+ + )); +} + +export default MetricsList; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsList/index.ts b/frontend/app/components/Dashboard/components/MetricsList/index.ts new file mode 100644 index 000000000..ad693888c --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricsList/index.ts @@ -0,0 +1 @@ +export { default } from './MetricsList'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx index d358075db..e00402d1f 100644 --- a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx @@ -1,14 +1,22 @@ import React from 'react'; -import { Button, PageTitle, Link } from 'UI'; +import { Button, PageTitle, Icon, Link } from 'UI'; import { withSiteId, dashboardMetricCreate } from 'App/routes'; +import MetricsList from '../MetricsList'; function MetricsView(props) { return (
-
+
{/* */} +
+ +
+
); } diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 8f03c4e59..854565e1b 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -5,6 +5,7 @@ import Widget from "./widget"; export default class DashboardStore { dashboards: Dashboard[] = [] + widgets: Widget[] = [] selectedDashboard: Dashboard | null = new Dashboard() isLoading: boolean = false siteId: any = null @@ -48,6 +49,17 @@ export default class DashboardStore { // this.selectedDashboard?.widgets[4].update({ position: 2 }) // this.selectedDashboard?.swapWidgetPosition(2, 0) // }, 3000) + + for (let i = 0; i < 8; i++) { + const widget = getRandomWidget(); + widget.position = i; + widget.name = `Widget ${i}`; + widget.isPrivate = [true, false][Math.floor(Math.random() * 2)]; + widget.dashboardIds = [this.selectedDashboard?.dashboardId]; + widget.owner = ["John", "Jane", "Jack", "Jill"][i % 4]; + widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; + this.widgets.push(widget); + } } resetCurrentWidget() { diff --git a/frontend/app/components/Dashboard/store/widget.ts b/frontend/app/components/Dashboard/store/widget.ts index 27f5dfd46..5b0329cab 100644 --- a/frontend/app/components/Dashboard/store/widget.ts +++ b/frontend/app/components/Dashboard/store/widget.ts @@ -11,6 +11,10 @@ export default class Widget { viewType: string = "lineChart" series: FilterSeries[] = [] sessions: [] = [] + isPrivate: boolean = false + owner: string = "" + lastModified: Date = new Date() + dashboardIds: any[] = [] position: number = 0 data: any = {} diff --git a/frontend/app/svg/icons/person-fill.svg b/frontend/app/svg/icons/person-fill.svg new file mode 100644 index 000000000..c9ef78372 --- /dev/null +++ b/frontend/app/svg/icons/person-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file From af39d9c32f7e7cf50afe44babb8ec28f7570e9c9 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 24 Mar 2022 16:02:41 +0100 Subject: [PATCH 15/87] feat(ui) - dashboards wip --- frontend/app/Router.js | 6 +-- frontend/app/assets/index.html | 1 + .../app/components/Dashboard/NewDashboard.tsx | 4 +- .../DashboardSideMenu/DashboardSideMenu.tsx | 2 +- .../DashboardView/DashboardView.tsx | 16 +++++- .../Dashboard/store/dashboardStore.ts | 17 ++++--- frontend/app/components/Modal/Modal.js | 30 +++++++---- frontend/app/components/Modal/ModalContext.js | 50 ++++++------------- .../app/components/Modal/ModalOverlay.tsx | 16 ++++++ frontend/app/components/Modal/useModal.ts | 15 ++++++ frontend/app/initialize.js | 9 ++-- 11 files changed, 105 insertions(+), 61 deletions(-) create mode 100644 frontend/app/components/Modal/ModalOverlay.tsx create mode 100644 frontend/app/components/Modal/useModal.ts diff --git a/frontend/app/Router.js b/frontend/app/Router.js index 74e8845f7..a2e36dd8f 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -186,9 +186,9 @@ class Router extends React.Component { } - - {/* */} - {/* */} + {/* */} + + {/* diff --git a/frontend/app/assets/index.html b/frontend/app/assets/index.html index a9d4b0f62..03300b45c 100644 --- a/frontend/app/assets/index.html +++ b/frontend/app/assets/index.html @@ -12,6 +12,7 @@ +

Loading...

diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 77a784ec2..46901a87f 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -30,7 +30,9 @@ function NewDashboard(props) { if (dashboardId) { store.selectDashboardById(dashboardId); } else { - store.selectDefaultDashboard(); + store.selectDefaultDashboard().then((resp) => { + history.push(withSiteId(dashboardSelected(resp.dashboardId), siteId)); + }); } } diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 7f223bb55..5a02b25b7 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -24,7 +24,7 @@ function DashboardSideMenu(props) { {store.dashboards.map(item => ( { + let { handleModal } = React.useContext(ModalContext); + return ( +
+ Hello this is test +
+ ) +} function DashboardView(props) { + let { handleModal } = React.useContext(ModalContext); const { store } = props; const dashboard = store.selectedDashboard const list = dashboard?.widgets; + useEffect(() => { + handleModal() + }, []) return (
diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 854565e1b..54e57d31c 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -217,14 +217,17 @@ export default class DashboardStore { } selectDefaultDashboard = () => { - if (this.dashboards.length > 0) { - const pinnedDashboard = this.dashboards.find(d => d.isPinned) - if (pinnedDashboard) { - this.selectedDashboard = pinnedDashboard - } else { - this.selectedDashboard = this.dashboards[0] + return new Promise((resolve, reject) => { + if (this.dashboards.length > 0) { + const pinnedDashboard = this.dashboards.find(d => d.isPinned) + if (pinnedDashboard) { + this.selectedDashboard = pinnedDashboard + } else { + this.selectedDashboard = this.dashboards[0] + } } - } + resolve(this.selectedDashboard) + }) } } diff --git a/frontend/app/components/Modal/Modal.js b/frontend/app/components/Modal/Modal.js index cb4738cd0..0d078cc69 100644 --- a/frontend/app/components/Modal/Modal.js +++ b/frontend/app/components/Modal/Modal.js @@ -1,13 +1,23 @@ -export default class Modal extends React.PureComponent { - constructor(props) { - super(props); - this.el = document.createElement('div'); - } +import React from "react"; +import ReactDOM from "react-dom"; +import { ModalContext } from "./modalContext"; +import ModalOverlay from "./ModalOverlay"; - render() { +const Modal = () => { + let { modalContent, handleModal, modal } = React.useContext(ModalContext); + if (modal) { return ReactDOM.createPortal( - this.props.children, - this.el, +
+ + {modalContent} + +
, + document.querySelector("#modal-root") ); - } -} \ No newline at end of file + } else return null; +}; + +export default Modal; diff --git a/frontend/app/components/Modal/ModalContext.js b/frontend/app/components/Modal/ModalContext.js index 197358f97..89c453a97 100644 --- a/frontend/app/components/Modal/ModalContext.js +++ b/frontend/app/components/Modal/ModalContext.js @@ -1,38 +1,18 @@ -const ModalContext = React.createContext({ - component: null, - props: {}, - content: null, - showModal: () => {}, - hideModal: () => {} -}); +import React from "react"; +import useModal from "./useModal"; +import Modal from "./modal"; -export class ModalProvider extends React.PureComponent { - showModal = (component, props = {}) => { - this.setState({ - component, - props - }); - }; +let ModalContext; +let { Provider } = (ModalContext = React.createContext()); - hideModal = () => this.setState({ - component: null, - props: {}, - }); +let ModalProvider = ({ children }) => { + let { modal, handleModal, modalContent } = useModal(); + return ( + + + {children} + + ); +}; - state = { - component: null, - props: {}, - showModal: this.showModal, - hideModal: this.hideModal - }; - - render() { - return ( - - {this.props.children} - - ); - } -} - -export const ModalConsumer = ModalContext.Consumer; +export { ModalContext, ModalProvider }; diff --git a/frontend/app/components/Modal/ModalOverlay.tsx b/frontend/app/components/Modal/ModalOverlay.tsx new file mode 100644 index 000000000..003f29ee6 --- /dev/null +++ b/frontend/app/components/Modal/ModalOverlay.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { ModalContext } from "App/components/Modal/modalContext"; +import useModal from 'App/components/Modal/useModal'; + +function ModalOverlay({ children }) { + let modal = useModal(); + // console.log('m', m); + + return ( +
modal.handleModal(false)} style={{ background: "rgba(0,0,0,0.8)", zIndex: '9999' }}> + {children} +
+ ); +} + +export default ModalOverlay; \ No newline at end of file diff --git a/frontend/app/components/Modal/useModal.ts b/frontend/app/components/Modal/useModal.ts new file mode 100644 index 000000000..edcf08e98 --- /dev/null +++ b/frontend/app/components/Modal/useModal.ts @@ -0,0 +1,15 @@ +import React from "react"; + +export default () => { + let [modal, setModal] = React.useState(false); + let [modalContent, setModalContent] = React.useState("I'm the Modal Content"); + + let handleModal = (content = false) => { + setModal(!modal); + if (content) { + setModalContent(content); + } + }; + + return { modal, handleModal, modalContent }; +}; diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index 96a527f14..026bb5e76 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -7,6 +7,7 @@ import store from './store'; import Router from './Router'; import DashboardStore from './components/Dashboard/store'; import { DashboardStoreProvider } from './components/Dashboard/store/store'; +import { ModalProvider } from './components/Modal/modalContext'; document.addEventListener('DOMContentLoaded', () => { @@ -14,9 +15,11 @@ document.addEventListener('DOMContentLoaded', () => { render( ( - - - + + + + + ), document.getElementById('app'), From b551fd508459ec272c8e6e32e47515c8ac674f1c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 25 Mar 2022 18:57:23 +0100 Subject: [PATCH 16/87] feat(ui) - dashboards wip --- .../Dashboard/WidgetWrapper/WidgetWrapper.tsx | 6 +- .../DashboardMetricSelection.tsx | 129 ++++++++++++++++++ .../DashboardMetricSelection/index.ts | 1 + .../DashboardModal/DashboardModal.tsx | 25 ++++ .../components/DashboardModal/index.ts | 1 + .../DashboardView/DashboardView.tsx | 19 +-- .../Dashboard/store/dashboardStore.ts | 33 +++-- frontend/app/components/Modal/Modal.js | 33 ++--- frontend/app/components/Modal/ModalContext.js | 52 +++++-- frontend/app/components/Modal/ModalRoot.js | 5 +- frontend/app/initialize.js | 14 +- frontend/app/styles/colors-autogen.css | 31 +++++ frontend/scripts/colors.js | 3 + 13 files changed, 283 insertions(+), 69 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardMetricSelection/index.ts create mode 100644 frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardModal/index.ts diff --git a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx index daade0774..d34eeb08d 100644 --- a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx @@ -5,9 +5,9 @@ import { ItemMenu } from 'UI'; function WidgetWrapper(props) { const { widget } = props; - const store: any = useDashboardStore(); - const dashboard = store.selectedDashboard; - const siteId = store.siteId; + // const store: any = useDashboardStore(); + // const dashboard = store.selectedDashboard; + // const siteId = store.siteId; return (
diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx new file mode 100644 index 000000000..ef4a9886e --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -0,0 +1,129 @@ +import React from 'react'; +import WidgetWrapper from '../../WidgetWrapper'; +import { useDashboardStore } from '../../store/store'; +import { useObserver } from 'mobx-react-lite'; +import cn from 'classnames'; +import { Button } from 'UI'; + +function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, unSelectCategory }) { + const selectedCategoryWidgetsCount = useObserver(() => { + return category.widgets.filter(widget => selectedWidgetIds.includes(widget.widgetId)).length; + }); + return ( +
onClick(category)} + > +
{category.name}
+
{category.description}
+ {selectedCategoryWidgetsCount > 0 && ( +
+ unSelectCategory(category)} /> + {`Selected ${selectedCategoryWidgetsCount} of ${category.widgets.length}`} +
+ )} +
+ ); +} + +function DashboardMetricSelection(props) { + const store: any = useDashboardStore(); + const widgetCategories = store?.widgetCategories; + const widgetTemplates = store?.widgetTemplates; + const [activeCategory, setActiveCategory] = React.useState(widgetCategories[0]); + const [selectedWidgets, setSelectedWidgets] = React.useState([]); + const selectedWidgetIds = selectedWidgets.map((widget: any) => widget.widgetId); + + const removeSelectedWidgetByCategory = (category: any) => { + const categoryWidgetIds = category.widgets.map((widget: any) => widget.widgetId); + const newSelectedWidgets = selectedWidgets.filter((widget: any) => !categoryWidgetIds.includes(widget.widgetId)); + setSelectedWidgets(newSelectedWidgets); + }; + + const toggleWidgetSelection = (widget: any) => { + console.log('toggleWidgetSelection', widget.widgetId); + if (selectedWidgetIds.includes(widget.widgetId)) { + setSelectedWidgets(selectedWidgets.filter((w: any) => w.widgetId !== widget.widgetId)); + } else { + setSelectedWidgets(selectedWidgets.concat(widget)); + } + }; + + const handleWidgetCategoryClick = (category: any) => { + setActiveCategory(category); + }; + + const toggleAllWidgets = ({ target: { checked }}) => { + if (checked == true) { + const allWidgets = widgetCategories.reduce((acc, category) => { + return acc.concat(category.widgets); + }, []); + + setSelectedWidgets(allWidgets); + } else { + setSelectedWidgets([]); + } + } + + return useObserver(() => ( +
+
+
+
Categories
+
+ +
+
+

Errors Tracking

+ 12 +
+ +
+ Showing past 7 days data for visual clue +
+ + Select All +
+
+
+
+
+
+
+ {widgetCategories.map((category, index) => + + )} +
+
+
+
+ {activeCategory.widgets.map((widget: any) => ( +
toggleWidgetSelection(widget)} + > + +
+ ))} +
+
+
+ +
+ +
+
+ )); +} + +export default DashboardMetricSelection; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/index.ts b/frontend/app/components/Dashboard/components/DashboardMetricSelection/index.ts new file mode 100644 index 000000000..4436d6bfc --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardMetricSelection'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx new file mode 100644 index 000000000..1cb87307a --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import WidgetWrapper from '../../WidgetWrapper'; +import { useDashboardStore } from '../../store/store'; +import { observer, useObserver } from 'mobx-react-lite'; +import cn from 'classnames'; +import { Button } from 'UI'; +import DashboardMetricSelection from '../DashboardMetricSelection'; + + + +function DashboardModal(props) { + const store: any = useDashboardStore(); + + + return useObserver(() => ( +
+
+

Add Metric to Dashboard

+
+ +
+ )); +} + +export default observer(DashboardModal); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardModal/index.ts b/frontend/app/components/Dashboard/components/DashboardModal/index.ts new file mode 100644 index 000000000..7082b746b --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardModal/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardModal' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index f99c4b537..09e6b5d3c 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -4,24 +4,17 @@ import { observer } from 'mobx-react-lite'; import { withDashboardStore } from '../../store/store'; import { Button, PageTitle, Link } from 'UI'; import { withSiteId, dashboardMetricCreate } from 'App/routes'; -import { ModalContext } from "App/components/Modal/modalContext"; - -const ModalContent = () => { - let { handleModal } = React.useContext(ModalContext); - return ( -
- Hello this is test -
- ) -} +import withModal from 'App/components/Modal/withModal'; +import DashboardModal from '../DashboardModal' function DashboardView(props) { - let { handleModal } = React.useContext(ModalContext); + // let { handleModal } = React.useContext(ModalContext); const { store } = props; const dashboard = store.selectedDashboard const list = dashboard?.widgets; useEffect(() => { - handleModal() + // handleModal() + props.showModal(DashboardModal) }, []) return (
@@ -36,4 +29,4 @@ function DashboardView(props) { ) } -export default withDashboardStore(observer(DashboardView)); \ No newline at end of file +export default withDashboardStore(withModal(observer(DashboardView))); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 54e57d31c..0085c8f41 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -2,14 +2,14 @@ import { makeAutoObservable, runInAction, observable, action, reaction } from "m import Dashboard from "./dashboard" import APIClient from 'App/api_client'; import Widget from "./widget"; - export default class DashboardStore { dashboards: Dashboard[] = [] - widgets: Widget[] = [] + widgetTemplates: any[] = [] selectedDashboard: Dashboard | null = new Dashboard() isLoading: boolean = false siteId: any = null currentWidget: Widget = new Widget() + widgetCategories: any[] = [] private client = new APIClient() @@ -50,16 +50,27 @@ export default class DashboardStore { // this.selectedDashboard?.swapWidgetPosition(2, 0) // }, 3000) - for (let i = 0; i < 8; i++) { - const widget = getRandomWidget(); - widget.position = i; - widget.name = `Widget ${i}`; - widget.isPrivate = [true, false][Math.floor(Math.random() * 2)]; - widget.dashboardIds = [this.selectedDashboard?.dashboardId]; - widget.owner = ["John", "Jane", "Jack", "Jill"][i % 4]; - widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; - this.widgets.push(widget); + for (let i = 0; i < 4; i++) { + const cat: any = { + name: `Category ${i + 1}`, + categoryId: i, + description: `Category ${i + 1} description`, + widgets: [] + } + // const randomBumberBetween = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min + const randomNumber = Math.floor(Math.random() * (5 - 2 + 1)) + 2 + + for (let j = 0; j < randomNumber; j++) { + const widget: any= {}; + widget.widgetId = `${i}-${j}` + widget.name = `Widget ${i}-${j}`; + widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; + cat.widgets.push(widget); + } + + this.widgetCategories.push(cat) } + } resetCurrentWidget() { diff --git a/frontend/app/components/Modal/Modal.js b/frontend/app/components/Modal/Modal.js index 0d078cc69..af0eff6b3 100644 --- a/frontend/app/components/Modal/Modal.js +++ b/frontend/app/components/Modal/Modal.js @@ -1,23 +1,16 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import { ModalContext } from "./modalContext"; -import ModalOverlay from "./ModalOverlay"; +import React from 'react'; +import ReactDOM from 'react-dom'; -const Modal = () => { - let { modalContent, handleModal, modal } = React.useContext(ModalContext); - if (modal) { +export default class Modal extends React.PureComponent { + constructor(props) { + super(props); + this.el = document.createElement('div'); + } + + render() { return ReactDOM.createPortal( -
- - {modalContent} - -
, - document.querySelector("#modal-root") + this.props.children, + this.el, ); - } else return null; -}; - -export default Modal; + } +} \ No newline at end of file diff --git a/frontend/app/components/Modal/ModalContext.js b/frontend/app/components/Modal/ModalContext.js index 89c453a97..43ab9f8b8 100644 --- a/frontend/app/components/Modal/ModalContext.js +++ b/frontend/app/components/Modal/ModalContext.js @@ -1,18 +1,40 @@ -import React from "react"; -import useModal from "./useModal"; -import Modal from "./modal"; +import React, { Component, createContext } from 'react'; -let ModalContext; -let { Provider } = (ModalContext = React.createContext()); +const ModalContext = createContext({ + component: null, + props: {}, + showModal: () => {}, + hideModal: () => {} +}); -let ModalProvider = ({ children }) => { - let { modal, handleModal, modalContent } = useModal(); - return ( - - - {children} - - ); -}; +export class ModalProvider extends Component { + showModal = (component, props = {}) => { + this.setState({ + component, + props + }); + }; -export { ModalContext, ModalProvider }; + hideModal = () => + this.setState({ + component: null, + props: {} + }); + + state = { + component: null, + props: {}, + showModal: this.showModal, + hideModal: this.hideModal + }; + + render() { + return ( + + {this.props.children} + + ); + } +} + +export const ModalConsumer = ModalContext.Consumer; diff --git a/frontend/app/components/Modal/ModalRoot.js b/frontend/app/components/Modal/ModalRoot.js index 226447af1..98152c59e 100644 --- a/frontend/app/components/Modal/ModalRoot.js +++ b/frontend/app/components/Modal/ModalRoot.js @@ -1,3 +1,6 @@ +import React from 'react'; +import { ModalConsumer } from './ModalContext'; + const ModalRoot = () => ( {({ component: Component, props, hideModal }) => @@ -6,4 +9,4 @@ const ModalRoot = () => ( ); -export default ModalRoot \ No newline at end of file +export default ModalRoot; diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index 026bb5e76..6460a9eff 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -7,7 +7,8 @@ import store from './store'; import Router from './Router'; import DashboardStore from './components/Dashboard/store'; import { DashboardStoreProvider } from './components/Dashboard/store/store'; -import { ModalProvider } from './components/Modal/modalContext'; +import { ModalProvider } from './components/Modal/ModalContext'; +import ModalRoot from './components/Modal/ModalRoot'; document.addEventListener('DOMContentLoaded', () => { @@ -15,11 +16,12 @@ document.addEventListener('DOMContentLoaded', () => { render( ( - - - - - + + + + + + ), document.getElementById('app'), diff --git a/frontend/app/styles/colors-autogen.css b/frontend/app/styles/colors-autogen.css index c7b3a6ce1..a98c7cef6 100644 --- a/frontend/app/styles/colors-autogen.css +++ b/frontend/app/styles/colors-autogen.css @@ -62,6 +62,37 @@ .color-white { color: $white } .color-borderColor { color: $borderColor } +/* border-color */ +.border-color-main { border-color: $main } +.border-color-gray-light-shade { border-color: $gray-light-shade } +.border-color-gray-lightest { border-color: $gray-lightest } +.border-color-gray-light { border-color: $gray-light } +.border-color-gray-medium { border-color: $gray-medium } +.border-color-gray-dark { border-color: $gray-dark } +.border-color-gray-darkest { border-color: $gray-darkest } +.border-color-teal { border-color: $teal } +.border-color-teal-dark { border-color: $teal-dark } +.border-color-teal-light { border-color: $teal-light } +.border-color-tealx { border-color: $tealx } +.border-color-tealx-light { border-color: $tealx-light } +.border-color-tealx-light-border { border-color: $tealx-light-border } +.border-color-orange { border-color: $orange } +.border-color-yellow { border-color: $yellow } +.border-color-yellow2 { border-color: $yellow2 } +.border-color-orange-dark { border-color: $orange-dark } +.border-color-green { border-color: $green } +.border-color-green2 { border-color: $green2 } +.border-color-green-dark { border-color: $green-dark } +.border-color-red { border-color: $red } +.border-color-red2 { border-color: $red2 } +.border-color-blue { border-color: $blue } +.border-color-blue2 { border-color: $blue2 } +.border-color-active-blue { border-color: $active-blue } +.border-color-active-blue-border { border-color: $active-blue-border } +.border-color-pink { border-color: $pink } +.border-color-white { border-color: $white } +.border-color-borderColor { border-color: $borderColor } + /* color */ .hover-main:hover { color: $main } .hover-gray-light-shade:hover { color: $gray-light-shade } diff --git a/frontend/scripts/colors.js b/frontend/scripts/colors.js index ac8eb69be..6071785a5 100644 --- a/frontend/scripts/colors.js +++ b/frontend/scripts/colors.js @@ -12,6 +12,9 @@ ${ colors.map(color => `.fill-${ color } { fill: $${ color } }`).join('\n') } /* color */ ${ colors.map(color => `.color-${ color } { color: $${ color } }`).join('\n') } +/* border-color */ +${ colors.map(color => `.border-color-${ color } { border-color: $${ color } }`).join('\n') } + /* color */ ${ colors.map(color => `.hover-${ color }:hover { color: $${ color } }`).join('\n') } `) From cc08060e308cec2bbec6f9e4d07aeccb7b28c812 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 25 Mar 2022 20:18:31 +0100 Subject: [PATCH 17/87] feat(api): dashboard 2/5 --- api/app.py | 3 +- api/chalicelib/core/dashboards2.py | 65 +++++++++++---- api/chalicelib/core/templates.py | 19 +++++ api/requirements.txt | 2 +- api/routers/core.py | 72 ---------------- api/routers/subs/dashboard.py | 27 +----- api/routers/subs/metrics.py | 129 +++++++++++++++++++++++++++++ api/schemas.py | 17 ++++ 8 files changed, 217 insertions(+), 117 deletions(-) create mode 100644 api/chalicelib/core/templates.py create mode 100644 api/routers/subs/metrics.py diff --git a/api/app.py b/api/app.py index 4d3697e92..b1ddca682 100644 --- a/api/app.py +++ b/api/app.py @@ -12,7 +12,7 @@ from routers import core, core_dynamic from routers.app import v1_api from routers.crons import core_crons from routers.crons import core_dynamic_crons -from routers.subs import dashboard, insights +from routers.subs import dashboard, insights, metrics app = FastAPI() @@ -54,6 +54,7 @@ app.include_router(core_dynamic.public_app) app.include_router(core_dynamic.app) app.include_router(core_dynamic.app_apikey) app.include_router(dashboard.app) +app.include_router(metrics.app) app.include_router(insights.app) app.include_router(v1_api.app_apikey) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 5f2bced2d..782532718 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -1,3 +1,5 @@ +import json + import schemas from chalicelib.utils import helper from chalicelib.utils import pg_client @@ -29,32 +31,59 @@ def get_dashboards(project_id, user_id): def get_dashboard(project_id, user_id, dashboard_id): with pg_client.PostgresClient() as cur: - pg_query = """SELECT dashboards.*, all_widgets.* + pg_query = """SELECT dashboards.*, all_template_widgets.widgets AS template_widgets, all_metric_widgets.widgets AS metric_widgets FROM dashboards - LEFT JOIN LATERAL (SELECT COALESCE(ARRAY_AGG(widgets), '{}') AS widgets - FROM widgets - INNER JOIN dashboard_widgets USING (widget_id) + LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG(templates), '[]'::jsonb) AS widgets + FROM templates + INNER JOIN dashboard_widgets USING (template_id) WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id - AND widgets.deleted_at ISNULL - AND (widgets.project_id ISNULL OR widgets.project_id = %(projectId)s) - ) AS all_widgets ON (TRUE) + ) AS all_template_widgets ON (TRUE) + LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG(raw_metrics), '[]') AS widgets + FROM (SELECT metrics.*, metric_series.series + FROM metrics + INNER JOIN dashboard_widgets USING (metric_id) + LEFT JOIN LATERAL (SELECT JSONB_AGG(metric_series.* ORDER BY index) AS series + FROM metric_series + WHERE metric_series.metric_id = metrics.metric_id + AND metric_series.deleted_at ISNULL + ) AS metric_series ON (TRUE) + WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id + AND metrics.deleted_at ISNULL + AND metrics.project_id = %(projectId)s) AS raw_metrics + ) AS all_metric_widgets ON (TRUE) WHERE dashboards.deleted_at ISNULL AND dashboards.project_id = %(projectId)s AND dashboard_id = %(dashboard_id)s AND (dashboards.user_id = %(userId)s OR is_public);""" params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id} + print(cur.mogrify(pg_query, params)) + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + row["widgets"] = row.pop("template_widgets") + row.pop("metric_widgets") + return helper.dict_to_camel_case(row) + + +def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashboardPayloadSchema): + ref_key = "metric_id" + if data.template_id is not None: + ref_key = "template_id" + with pg_client.PostgresClient() as cur: + pg_query = f"""INSERT INTO dashboard_widgets(dashboard_id, {ref_key}, user_id, configuration, name) + VALUES (%(dashboard_id)s, %({ref_key})s, %(userId)s, %(configuration)s::jsonb, %(name)s) + RETURNING *;""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()} + params["configuration"] = json.dumps(params.get("configuration", {})) cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() return helper.dict_to_camel_case(row) - -def get_widgets(project_id): - with pg_client.PostgresClient() as cur: - pg_query = f"""SELECT * - FROM widgets - WHERE deleted_at ISNULL - AND project_id = %(projectId)s;""" - params = {"projectId": project_id} - cur.execute(cur.mogrify(pg_query, params)) - rows = cur.fetchall() - return helper.list_to_camel_case(rows) +# def get_widgets(project_id): +# with pg_client.PostgresClient() as cur: +# pg_query = f"""SELECT * +# FROM widgets +# WHERE deleted_at ISNULL +# AND project_id = %(projectId)s;""" +# params = {"projectId": project_id} +# cur.execute(cur.mogrify(pg_query, params)) +# rows = cur.fetchall() +# return helper.list_to_camel_case(rows) diff --git a/api/chalicelib/core/templates.py b/api/chalicelib/core/templates.py new file mode 100644 index 000000000..8141bdd35 --- /dev/null +++ b/api/chalicelib/core/templates.py @@ -0,0 +1,19 @@ +from chalicelib.utils import helper +from chalicelib.utils import pg_client + +CATEGORY_DESCRIPTION = { + 'categ1': 'lorem', +} + + +def get_templates(): + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT category, jsonb_agg(templates ORDER BY name) AS widgets + FROM templates + GROUP BY category + ORDER BY category;""" + cur.execute(pg_query) + rows = cur.fetchall() + for r in rows: + r["description"] = CATEGORY_DESCRIPTION.get(r["category"], "") + return helper.list_to_camel_case(rows) diff --git a/api/requirements.txt b/api/requirements.txt index 4af962f4f..e5672d80a 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -8,7 +8,7 @@ jira==2.0.0 -fastapi==0.74.1 +fastapi==0.75.0 uvicorn[standard]==0.17.5 python-decouple==3.6 pydantic[email]==1.8.2 diff --git a/api/routers/core.py b/api/routers/core.py index 97a749429..53fdba667 100644 --- a/api/routers/core.py +++ b/api/routers/core.py @@ -1065,78 +1065,6 @@ def change_client_password(data: schemas.EditUserPasswordSchema = Body(...), user_id=context.user_id) -@app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"]) -@app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"]) -def try_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): - return {"data": custom_metrics.merged_live(project_id=projectId, data=data)} - - -@app.post('/{projectId}/custom_metrics', tags=["customMetrics"]) -@app.put('/{projectId}/custom_metrics', tags=["customMetrics"]) -def add_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): - return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data) - - -@app.get('/{projectId}/custom_metrics', tags=["customMetrics"]) -def get_custom_metrics(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": custom_metrics.get_all(project_id=projectId, user_id=context.user_id)} - - -@app.get('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"]) -def get_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)): - data = custom_metrics.get(project_id=projectId, user_id=context.user_id, metric_id=metric_id) - if data is None: - return {"errors": ["custom metric not found"]} - return {"data": data} - - -@app.post('/{projectId}/custom_metrics/{metric_id}/sessions', tags=["customMetrics"]) -def get_custom_metric_sessions(projectId: int, metric_id: int, - data: schemas.CustomMetricSessionsPayloadSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): - data = custom_metrics.get_sessions(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data) - if data is None: - return {"errors": ["custom metric not found"]} - return {"data": data} - - -@app.post('/{projectId}/custom_metrics/{metric_id}/chart', tags=["customMetrics"]) -def get_custom_metric_chart(projectId: int, metric_id: int, data: schemas.CustomMetricChartPayloadSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): - data = custom_metrics.make_chart(project_id=projectId, user_id=context.user_id, metric_id=metric_id, - data=data) - if data is None: - return {"errors": ["custom metric not found"]} - return {"data": data} - - -@app.post('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"]) -@app.put('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"]) -def update_custom_metric(projectId: int, metric_id: int, data: schemas.UpdateCustomMetricsSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): - data = custom_metrics.update(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data) - if data is None: - return {"errors": ["custom metric not found"]} - return {"data": data} - - -@app.post('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"]) -@app.put('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"]) -def update_custom_metric_state(projectId: int, metric_id: int, - data: schemas.UpdateCustomMetricsStatusSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): - return { - "data": custom_metrics.change_state(project_id=projectId, user_id=context.user_id, metric_id=metric_id, - status=data.active)} - - -@app.delete('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"]) -def delete_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": custom_metrics.delete(project_id=projectId, user_id=context.user_id, metric_id=metric_id)} - - @app.post('/{projectId}/saved_search', tags=["savedSearch"]) @app.put('/{projectId}/saved_search', tags=["savedSearch"]) def add_saved_search(projectId: int, data: schemas.SavedSearchSchema = Body(...), diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 1f5e827e4..169893693 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -1,10 +1,9 @@ -from fastapi import Body, Depends +from fastapi import Body import schemas -from chalicelib.core import dashboard, dashboards2 +from chalicelib.core import dashboard from chalicelib.core import metadata from chalicelib.utils import helper -from or_dependencies import OR_context from routers.base import get_routers public_app, app, app_apikey = get_routers() @@ -345,25 +344,3 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body *helper.explode_widget(dashboard.get_avg_cpu(project_id=projectId, **data.dict())), *helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())), ]} - - -@app.post('/{projectId}/dashboards', tags=["dashboard", "metrics"]) -@app.put('/{projectId}/dashboards', tags=["dashboard", "metrics"]) -def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): - return {"data": dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)} - - -@app.get('/{projectId}/dashboards', tags=["dashboard", "metrics"]) -def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": dashboards2.get_dashboards(project_id=projectId, user_id=context.user_id)} - - -@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"]) -def get_dashboards(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} - - -@app.get('/{projectId}/widgets', tags=["dashboard", "metrics"]) -def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": dashboards2.get_widgets(project_id=projectId)} diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py new file mode 100644 index 000000000..9c0bb64ba --- /dev/null +++ b/api/routers/subs/metrics.py @@ -0,0 +1,129 @@ +from fastapi import Body, Depends + +import schemas +from chalicelib.core import dashboards2, templates, custom_metrics +from or_dependencies import OR_context +from routers.base import get_routers + +public_app, app, app_apikey = get_routers() + + +@app.post('/{projectId}/dashboards', tags=["dashboard", "metrics"]) +@app.put('/{projectId}/dashboards', tags=["dashboard", "metrics"]) +def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)} + + +@app.get('/{projectId}/dashboards', tags=["dashboard", "metrics"]) +def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.get_dashboards(project_id=projectId, user_id=context.user_id)} + + +@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"]) +def get_dashboards(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} + + +@app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"]) +@app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"]) +def add_widget_to_dashboards(projectId: int, dashboardId: int, + data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + data=data)} + + +# @app.get('/{projectId}/widgets', tags=["dashboard", "metrics"]) +# def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): +# return {"data": dashboards2.get_widgets(project_id=projectId)} + + +@app.get('/{projectId}/metrics/templates', tags=["dashboard", "metrics"]) +def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": templates.get_templates()} + + +@app.post('/{projectId}/metrics/try', tags=["dashboard"]) +@app.put('/{projectId}/metrics/try', tags=["dashboard"]) +@app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"]) +@app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"]) +def try_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return {"data": custom_metrics.merged_live(project_id=projectId, data=data)} + + +@app.post('/{projectId}/metrics', tags=["dashboard"]) +@app.put('/{projectId}/metrics', tags=["dashboard"]) +@app.post('/{projectId}/custom_metrics', tags=["customMetrics"]) +@app.put('/{projectId}/custom_metrics', tags=["customMetrics"]) +def add_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data) + + +@app.get('/{projectId}/metrics', tags=["dashboard"]) +@app.get('/{projectId}/custom_metrics', tags=["customMetrics"]) +def get_custom_metrics(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": custom_metrics.get_all(project_id=projectId, user_id=context.user_id)} + + +@app.get('/{projectId}/metrics/{metric_id}', tags=["dashboard"]) +@app.get('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"]) +def get_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)): + data = custom_metrics.get(project_id=projectId, user_id=context.user_id, metric_id=metric_id) + if data is None: + return {"errors": ["custom metric not found"]} + return {"data": data} + + +@app.post('/{projectId}/metrics/{metric_id}/sessions', tags=["dashboard"]) +@app.post('/{projectId}/custom_metrics/{metric_id}/sessions', tags=["customMetrics"]) +def get_custom_metric_sessions(projectId: int, metric_id: int, + data: schemas.CustomMetricSessionsPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + data = custom_metrics.get_sessions(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data) + if data is None: + return {"errors": ["custom metric not found"]} + return {"data": data} + + +@app.post('/{projectId}/metrics/{metric_id}/chart', tags=["dashboard"]) +@app.post('/{projectId}/custom_metrics/{metric_id}/chart', tags=["customMetrics"]) +def get_custom_metric_chart(projectId: int, metric_id: int, data: schemas.CustomMetricChartPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + data = custom_metrics.make_chart(project_id=projectId, user_id=context.user_id, metric_id=metric_id, + data=data) + if data is None: + return {"errors": ["custom metric not found"]} + return {"data": data} + + +@app.post('/{projectId}/metrics/{metric_id}', tags=["dashboard"]) +@app.put('/{projectId}/metrics/{metric_id}', tags=["dashboard"]) +@app.post('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"]) +@app.put('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"]) +def update_custom_metric(projectId: int, metric_id: int, data: schemas.UpdateCustomMetricsSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + data = custom_metrics.update(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data) + if data is None: + return {"errors": ["custom metric not found"]} + return {"data": data} + + +@app.post('/{projectId}/metrics/{metric_id}/status', tags=["dashboard"]) +@app.put('/{projectId}/metrics/{metric_id}/status', tags=["dashboard"]) +@app.post('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"]) +@app.put('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"]) +def update_custom_metric_state(projectId: int, metric_id: int, + data: schemas.UpdateCustomMetricsStatusSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return { + "data": custom_metrics.change_state(project_id=projectId, user_id=context.user_id, metric_id=metric_id, + status=data.active)} + + +@app.delete('/{projectId}/metrics/{metric_id}', tags=["dashboard"]) +@app.delete('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"]) +def delete_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": custom_metrics.delete(project_id=projectId, user_id=context.user_id, metric_id=metric_id)} diff --git a/api/schemas.py b/api/schemas.py index 61b1f4056..bb111d61b 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -884,3 +884,20 @@ class CreateDashboardSchema(BaseModel): class Config: alias_generator = attribute_to_camel_case + + +class AddWidgetToDashboardPayloadSchema(BaseModel): + template_id: Optional[int] = Field(default=None) + metric_id: Optional[int] = Field(default=None) + name: Optional[str] = Field(default=None) + configuration: dict = Field(default={}) + + @root_validator + def validator(cls, values): + assert bool(values.get("template_id") is not None) != bool(values.get("metric_id") is not None), \ + f"templateId or metricId should be provided, but not both at the same time" + + return values + + class Config: + alias_generator = attribute_to_camel_case From 81e0aff1fd8a8f410936726fd19e0c09abdef890 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 28 Mar 2022 15:07:08 +0200 Subject: [PATCH 18/87] feat(ui) - dashboards wip --- .../DashboardForm/DashboardForm.tsx | 59 +++++++++++++++++++ .../components/DashboardForm/index.ts | 1 + .../DashboardMetricSelection.tsx | 8 +-- .../DashboardModal/DashboardModal.tsx | 22 ++++--- .../DashboardView/DashboardView.tsx | 3 +- .../components/MetricsList/MetricsList.tsx | 9 ++- .../components/Dashboard/store/dashboard.ts | 21 +++++-- .../Dashboard/store/dashboardStore.ts | 15 ++++- 8 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/DashboardForm/DashboardForm.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardForm/index.ts diff --git a/frontend/app/components/Dashboard/components/DashboardForm/DashboardForm.tsx b/frontend/app/components/Dashboard/components/DashboardForm/DashboardForm.tsx new file mode 100644 index 000000000..9765424eb --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardForm/DashboardForm.tsx @@ -0,0 +1,59 @@ +import { useObserver } from 'mobx-react-lite'; +import React from 'react'; +import { Input } from 'UI'; +import { useDashboardStore } from '../../store/store'; +import cn from 'classnames'; + +interface Props { +} + +function DashboardForm(props) { + const store: any = useDashboardStore(); + const dashboard = store.newDashboard; + + const write = ({ target: { value, name } }) => dashboard.update({ [ name ]: value }) + const writeRadio = ({ target: { value, name } }) => { + dashboard.update({ [name]: value === 'team' }); + } + + return useObserver(() => ( +
+
+ + +
+ +
+ + +
+ + + +
+
+
+ )); +} + +export default DashboardForm; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardForm/index.ts b/frontend/app/components/Dashboard/components/DashboardForm/index.ts new file mode 100644 index 000000000..01c5b0072 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardForm/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardForm'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index ef4a9886e..08984e96e 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -103,7 +103,7 @@ function DashboardMetricSelection(props) {
-
+
{activeCategory.widgets.map((widget: any) => (
- -
- -
)); } diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 1cb87307a..18e191e5e 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -1,25 +1,31 @@ import React from 'react'; -import WidgetWrapper from '../../WidgetWrapper'; import { useDashboardStore } from '../../store/store'; -import { observer, useObserver } from 'mobx-react-lite'; -import cn from 'classnames'; -import { Button } from 'UI'; +import { useObserver } from 'mobx-react-lite'; import DashboardMetricSelection from '../DashboardMetricSelection'; - +import DashboardForm from '../DashboardForm'; +import { Button } from 'UI'; function DashboardModal(props) { const store: any = useDashboardStore(); - + const dashbaord = useObserver(() => store.newDashboard); return useObserver(() => (
-

Add Metric to Dashboard

+

Create Dashboard

+ +

Create new dashboard by choosing from the range of predefined metrics that you care about. You can always add your custom metrics later.

+ +
+ +
)); } -export default observer(DashboardModal); \ No newline at end of file +export default DashboardModal; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index 09e6b5d3c..7b32fda56 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -13,8 +13,7 @@ function DashboardView(props) { const dashboard = store.selectedDashboard const list = dashboard?.widgets; useEffect(() => { - // handleModal() - props.showModal(DashboardModal) + // props.showModal(DashboardModal) }, []) return (
diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index 5f7c19b17..c25b2546c 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -8,6 +8,13 @@ function MetricsList(props: Props) { const store: any = useDashboardStore(); const widgets = store.widgets; const lenth = widgets.length; + const currentPage = store.metricsPage; + const totalPages = widgets.length; + + const pageSize = store.metricsPageSize; + const start = (currentPage - 1) * pageSize; + const end = currentPage * pageSize; + const list = widgets.slice(start, end); return useObserver(() => ( @@ -21,7 +28,7 @@ function MetricsList(props: Props) {
Last Modified
- {widgets.map((metric: any) => ( + {list.map((metric: any) => (
diff --git a/frontend/app/components/Dashboard/store/dashboard.ts b/frontend/app/components/Dashboard/store/dashboard.ts index b8113f3ba..c8f4a498b 100644 --- a/frontend/app/components/Dashboard/store/dashboard.ts +++ b/frontend/app/components/Dashboard/store/dashboard.ts @@ -5,7 +5,7 @@ import Widget from "./widget" export default class Dashboard { dashboardId: any = undefined name: string = "New Dashboard" - isPriavte: boolean = false + isPublic: boolean = false widgets: Widget[] = [] isValid: boolean = false isPinned: boolean = false @@ -14,7 +14,7 @@ export default class Dashboard { constructor() { makeAutoObservable(this, { name: observable, - isPriavte: observable, + isPublic: observable, widgets: observable, isValid: observable, @@ -31,14 +31,24 @@ export default class Dashboard { validate: action, sortWidgets: action, swapWidgetPosition: action, + update: action, }) + + this.validate(); + } + + update(data: any) { + runInAction(() => { + Object.assign(this, data) + }) + this.validate() } toJson() { return { dashboardId: this.dashboardId, name: this.name, - isPrivate: this.isPriavte, + isPrivate: this.isPublic, widgets: this.widgets.map(w => w.toJson()) } } @@ -47,14 +57,15 @@ export default class Dashboard { runInAction(() => { this.dashboardId = json.dashboardId this.name = json.name - this.isPriavte = json.isPrivate + this.isPublic = json.isPrivate this.widgets = json.widgets.map(w => new Widget().fromJson(w)) }) return this } validate() { - this.isValid = this.name.length > 0 + console.log('called...') + return this.isValid = this.name.length > 0 } addWidget(widget: Widget) { diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 0085c8f41..44b33f2f7 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -6,10 +6,14 @@ export default class DashboardStore { dashboards: Dashboard[] = [] widgetTemplates: any[] = [] selectedDashboard: Dashboard | null = new Dashboard() + newDashboard: Dashboard = new Dashboard() isLoading: boolean = false siteId: any = null currentWidget: Widget = new Widget() widgetCategories: any[] = [] + widgets: Widget[] = [] + metricsPage: number = 1 + metricsPageSize: number = 10 private client = new APIClient() @@ -50,6 +54,14 @@ export default class DashboardStore { // this.selectedDashboard?.swapWidgetPosition(2, 0) // }, 3000) + for (let i = 0; i < 20; i++) { + const widget: any= {}; + widget.widgetId = `${i}` + widget.name = `Widget ${i}`; + widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; + this.widgets.push(widget) + } + for (let i = 0; i < 4; i++) { const cat: any = { name: `Category ${i + 1}`, @@ -57,9 +69,8 @@ export default class DashboardStore { description: `Category ${i + 1} description`, widgets: [] } - // const randomBumberBetween = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min - const randomNumber = Math.floor(Math.random() * (5 - 2 + 1)) + 2 + const randomNumber = Math.floor(Math.random() * (5 - 2 + 1)) + 2 for (let j = 0; j < randomNumber; j++) { const widget: any= {}; widget.widgetId = `${i}-${j}` From 8e1f93630ce44c0f7606761e47860ee0212824df Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 28 Mar 2022 19:55:06 +0200 Subject: [PATCH 19/87] feat(api): dashboard re-think 1/5 --- api/app.py | 3 +- api/chalicelib/core/dashboards2.py | 24 +++-- api/chalicelib/core/templates.py | 9 +- api/routers/app/__init__.py | 0 api/routers/subs/metrics.py | 18 +++- api/routers/{app => subs}/v1_api.py | 0 .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 102 +++++++++++++----- .../db/init_dbs/postgresql/init_schema.sql | 14 ++- 8 files changed, 119 insertions(+), 51 deletions(-) delete mode 100644 api/routers/app/__init__.py rename api/routers/{app => subs}/v1_api.py (100%) diff --git a/api/app.py b/api/app.py index b1ddca682..ae7f051bf 100644 --- a/api/app.py +++ b/api/app.py @@ -9,10 +9,9 @@ from starlette.responses import StreamingResponse from chalicelib.utils import helper from chalicelib.utils import pg_client from routers import core, core_dynamic -from routers.app import v1_api from routers.crons import core_crons from routers.crons import core_dynamic_crons -from routers.subs import dashboard, insights, metrics +from routers.subs import dashboard, insights, metrics, v1_api app = FastAPI() diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 782532718..c36293059 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -77,13 +77,17 @@ def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashb row = cur.fetchone() return helper.dict_to_camel_case(row) -# def get_widgets(project_id): -# with pg_client.PostgresClient() as cur: -# pg_query = f"""SELECT * -# FROM widgets -# WHERE deleted_at ISNULL -# AND project_id = %(projectId)s;""" -# params = {"projectId": project_id} -# cur.execute(cur.mogrify(pg_query, params)) -# rows = cur.fetchall() -# return helper.list_to_camel_case(rows) +def remove_widget(project_id, user_id, dashboard_id,widget_id): + ref_key = "metric_id" + if data.template_id is not None: + ref_key = "template_id" + with pg_client.PostgresClient() as cur: + pg_query = f"""INSERT INTO dashboard_widgets(dashboard_id, {ref_key}, user_id, configuration, name) + VALUES (%(dashboard_id)s, %({ref_key})s, %(userId)s, %(configuration)s::jsonb, %(name)s) + RETURNING *;""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()} + params["configuration"] = json.dumps(params.get("configuration", {})) + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + diff --git a/api/chalicelib/core/templates.py b/api/chalicelib/core/templates.py index 8141bdd35..8e42668d3 100644 --- a/api/chalicelib/core/templates.py +++ b/api/chalicelib/core/templates.py @@ -6,12 +6,13 @@ CATEGORY_DESCRIPTION = { } -def get_templates(): +def get_templates(project_id, user_id): with pg_client.PostgresClient() as cur: - pg_query = f"""SELECT category, jsonb_agg(templates ORDER BY name) AS widgets - FROM templates + pg_query = cur.mogrify(f"""SELECT category, jsonb_agg(metrics ORDER BY name) AS widgets + FROM metrics + WHERE project_id ISNULL OR (project_id = %(project_id)s AND (is_public OR user_id= %(userId)s)) GROUP BY category - ORDER BY category;""" + ORDER BY category;""", {"project_id": project_id, "userId": user_id}) cur.execute(pg_query) rows = cur.fetchall() for r in rows: diff --git a/api/routers/app/__init__.py b/api/routers/app/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 9c0bb64ba..56b486977 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -21,15 +21,23 @@ def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_ @app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"]) -def get_dashboards(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): +def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} @app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"]) @app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"]) -def add_widget_to_dashboards(projectId: int, dashboardId: int, - data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): +def add_widget_to_dashboard(projectId: int, dashboardId: int, + data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + data=data)} + + +@app.delete('/{projectId}/dashboards/{dashboardId}/metrics/{metricId}', tags=["dashboard", "metrics"]) +def remove_widget_from_dashboard(projectId: int, dashboardId: int, metricId: int, + data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} @@ -41,7 +49,7 @@ def add_widget_to_dashboards(projectId: int, dashboardId: int, @app.get('/{projectId}/metrics/templates', tags=["dashboard", "metrics"]) def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": templates.get_templates()} + return {"data": templates.get_templates(project_id=projectId, user_id=context.user_id)} @app.post('/{projectId}/metrics/try', tags=["dashboard"]) diff --git a/api/routers/app/v1_api.py b/api/routers/subs/v1_api.py similarity index 100% rename from api/routers/app/v1_api.py rename to api/routers/subs/v1_api.py diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index a8bb8aafd..d0d31ecce 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -6,38 +6,86 @@ SELECT 'v1.5.5' $$ LANGUAGE sql IMMUTABLE; -CREATE TABLE dashboards +-- CREATE TABLE dashboards +-- ( +-- dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, +-- project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, +-- user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, +-- name text NOT NULL, +-- is_public boolean NOT NULL DEFAULT TRUE, +-- is_pinned boolean NOT NULL DEFAULT FALSE, +-- created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), +-- deleted_at timestamp NULL DEFAULT NULL +-- ); + +-- CREATE TABLE templates +-- ( +-- template_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, +-- template_key text, +-- name text NOT NULL, +-- category text NOT NULL, +-- series jsonb NOT NULL, +-- config jsonb NOT NULL, +-- predefined boolean DEFAULT TRUE +-- ); + +-- CREATE TABLE dashboard_widgets +-- ( +-- widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, +-- dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, +-- metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, +-- -- template_id integer NOT NULL REFERENCES templates (template_id) ON DELETE CASCADE, +-- user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, +-- created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), +-- configuration jsonb NOT NULL DEFAULT '{}'::jsonb, +-- name text +-- ); + +-- INSERT INTO public.templates (name, category, series, config, predefined, template_key) +-- VALUES ('captured sessions', 'overview', '[]', '{}', true, 'count_sessions'), +-- ('request load time', 'overview', '[]', '{}', true, 'avg_request_load_time'), +-- ('page load time', 'overview', '[]', '{}', true, 'avg_page_load_time'), +-- ('image load time', 'overview', '[]', '{}', true, 'avg_image_load_time'); + +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; + +ALTER TABLE IF EXISTS metrics + DROP CONSTRAINT IF EXISTS null_project_id_for_template_only; + +ALTER TABLE IF EXISTS metrics + ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', + ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS key text NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, + ALTER COLUMN project_id DROP NOT NULL, + ADD CONSTRAINT null_project_id_for_template_only + CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ); + + + +CREATE TABLE IF NOT EXISTS dashboard_widgets ( - dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, + metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, - name text NOT NULL, - is_public boolean NOT NULL DEFAULT TRUE, - is_pinned boolean NOT NULL DEFAULT FALSE, created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), - deleted_at timestamp NULL DEFAULT NULL + config jsonb NOT NULL DEFAULT '{}'::jsonb, + name text ); +-- INSERT INTO public.templates (name, category, series, config, predefined, template_key) +-- VALUES ('captured sessions', 'overview', '[]', '{}', true, 'count_sessions'), +-- ('request load time', 'overview', '[]', '{}', true, 'avg_request_load_time'), +-- ('page load time', 'overview', '[]', '{}', true, 'avg_page_load_time'), +-- ('image load time', 'overview', '[]', '{}', true, 'avg_image_load_time'); -CREATE TABLE widgets -( - widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, - user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, - name text NOT NULL, - is_template boolean NOT NULL DEFAULT FALSE, - predefined boolean NOT NULL DEFAULT FALSE, - created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), - deleted_at timestamp NULL DEFAULT NULL -); +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) +VALUES ('captured sessions', 'overview', '{}', true, true, true, 'count_sessions'), + ('request load time', 'overview', '{}', true, true, true, 'avg_request_load_time'), + ('page load time', 'overview', '{}', true, true, true, 'avg_page_load_time'), + ('image load time', 'overview', '{}', true, true, true, 'avg_image_load_time'); -CREATE TABLE dashboard_widgets -( - dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, - widget_id integer NOT NULL REFERENCES widgets (widget_id) ON DELETE CASCADE, - user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, - created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), - configuration jsonb NOT NULL DEFAULT '{}'::jsonb -); -COMMIT; \ No newline at end of file +COMMIT \ No newline at end of file diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 834a79968..52f367e8e 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -936,11 +936,11 @@ $$ CREATE INDEX jobs_project_id_idx ON jobs (project_id); CREATE TYPE metric_type AS ENUM ('timeseries','table'); - CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart'); + CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart'); CREATE TABLE metrics ( metric_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + project_id integer NULL REFERENCES projects (project_id) ON DELETE CASCADE, user_id integer REFERENCES users (user_id) ON DELETE SET NULL, name text NOT NULL, is_public boolean NOT NULL DEFAULT FALSE, @@ -951,8 +951,16 @@ $$ view_type metric_view_type NOT NULL DEFAULT 'lineChart', metric_of text NOT NULL DEFAULT 'sessionCount', metric_value text[] NOT NULL DEFAULT '{}'::text[], - metric_format text + metric_format text, + category text NULL DEFAULT 'custom', + is_predefined boolean NOT NULL DEFAULT FALSE, + is_template boolean NOT NULL DEFAULT FALSE, + key text NULL DEFAULT NULL, + config jsonb NOT NULL DEFAULT '{}'::jsonb, + CONSTRAINT null_project_id_for_template_only + CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ) ); + CREATE INDEX metrics_user_id_is_public_idx ON public.metrics (user_id, is_public); CREATE TABLE metric_series ( From 6607d3cb7454c5798df95f230a264fe65555f568 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 29 Mar 2022 12:10:47 +0200 Subject: [PATCH 20/87] feat(ui) - dashboards - widget drap and other changes --- .../BugFinder/EventFilter/EventEditor.js | 6 +- .../BugFinder/EventFilter/EventFilter.js | 4 +- .../app/components/Dashboard/NewDashboard.tsx | 19 ++----- .../Dashboard/WidgetWrapper/WidgetWrapper.tsx | 57 ++++++++++++++++--- .../DashboardView/DashboardView.tsx | 16 ++++-- .../DashboardWidgetGrid.tsx | 41 +++++++++++++ .../components/DashboardWidgetGrid/index.ts | 1 + .../components/MetricsList/MetricsList.tsx | 24 ++++++-- .../MetricsSearch/MetricsSearch.tsx | 25 ++++++++ .../components/MetricsSearch/index.ts | 1 + .../components/MetricsView/MetricsView.tsx | 8 +-- .../components/Dashboard/store/dashboard.ts | 1 + .../Dashboard/store/dashboardStore.ts | 12 ++-- frontend/app/components/hocs/dnd.js | 6 +- .../shared/EventFilter/EventEditor.js | 6 +- .../shared/EventFilter/EventFilter.js | 4 +- frontend/app/initialize.js | 17 ++++-- 17 files changed, 188 insertions(+), 60 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardWidgetGrid/index.ts create mode 100644 frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx create mode 100644 frontend/app/components/Dashboard/components/MetricsSearch/index.ts diff --git a/frontend/app/components/BugFinder/EventFilter/EventEditor.js b/frontend/app/components/BugFinder/EventFilter/EventEditor.js index def086416..29488a231 100644 --- a/frontend/app/components/BugFinder/EventFilter/EventEditor.js +++ b/frontend/app/components/BugFinder/EventFilter/EventEditor.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { DNDSource, DNDTarget } from 'Components/hocs/dnd'; +// import { DNDSource, DNDTarget } from 'Components/hocs/dnd'; import Event, { TYPES } from 'Types/filter/event'; import { operatorOptions } from 'Types/filter'; import { editEvent, removeEvent, clearEvents, applyFilter } from 'Duck/filters'; @@ -25,8 +25,8 @@ const getLabel = ({ type }) => { return getPlaceholder({ type }); }; -@DNDTarget('event') -@DNDSource('event') +// @DNDTarget('event') +// @DNDSource('event') @connect(state => ({ isLastEvent: state.getIn([ 'filters', 'appliedFilter', 'events' ]).size === 1, }), { editEvent, removeEvent, clearEvents, applyFilter }) diff --git a/frontend/app/components/BugFinder/EventFilter/EventFilter.js b/frontend/app/components/BugFinder/EventFilter/EventFilter.js index 5bc1d1b32..5adc5a42e 100644 --- a/frontend/app/components/BugFinder/EventFilter/EventFilter.js +++ b/frontend/app/components/BugFinder/EventFilter/EventFilter.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { Input } from 'semantic-ui-react'; -import { DNDContext } from 'Components/hocs/dnd'; +// import { DNDContext } from 'Components/hocs/dnd'; import { addEvent, applyFilter, moveEvent, clearEvents, edit, addCustomFilter, addAttribute, setSearchQuery, setActiveFlow, setFilterOption @@ -45,7 +45,7 @@ import SaveFilterButton from 'Shared/SaveFilterButton'; setBlink, edit, }) -@DNDContext +// @DNDContext export default class EventFilter extends React.PureComponent { state = { search: '', showFilterModal: false, showPlacehoder: true } fetchEventList = debounce(this.props.fetchEventList, 500) diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 46901a87f..5d51e5037 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -26,7 +26,10 @@ function NewDashboard(props) { }, []); useEffect(() => { - if (!dashboard || !dashboard.dashboardId) { + if (dashboardId) { + store.selectDashboardById(dashboardId); + } + if (!dashboardId) { if (dashboardId) { store.selectDashboardById(dashboardId); } else { @@ -35,19 +38,9 @@ function NewDashboard(props) { }); } } + }, []); - // if (dashboard) { - // if (dashboard.dashboardId !== dashboardId) { - // history.push(withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId))); - // } - - // history.replace(withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId))); - // } - - // console.log('dashboard', dashboard) - }, [dashboard]); - - console.log('rendering dashboard', props.match.params); + // console.log('rendering dashboard', props.match.params); return ( <> diff --git a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx index d34eeb08d..d7f6e2fe4 100644 --- a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx @@ -1,18 +1,61 @@ -import React from 'react'; +import React, { useRef } from 'react'; import { useDashboardStore } from '../store/store'; import cn from 'classnames'; import { ItemMenu } from 'UI'; +import { useDrag, useDrop } from 'react-dnd'; function WidgetWrapper(props) { - const { widget } = props; - // const store: any = useDashboardStore(); - // const dashboard = store.selectedDashboard; - // const siteId = store.siteId; + const { widget, index, moveListItem } = props; + + // useDrag - the list item is draggable + const [{ opacity, isDragging }, dragRef] = useDrag({ + type: 'item', + item: { index }, + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + opacity: monitor.isDragging() ? 0.5 : 1, + }), + }, [index]); + + // useDrop - the list item is also a drop area + const [spec, dropRef] = useDrop({ + accept: 'item', + drop: (item: any) => { + if (item.index === index) return; + moveListItem(item.index, index); + }, + // hover: (item: any, monitor: any) => { + // const dragIndex = item.index + // const hoverIndex = index + // const hoverBoundingRect = ref.current?.getBoundingClientRect() + // const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 + // const hoverActualY = monitor.getClientOffset().y - hoverBoundingRect.top + + // // if dragging down, continue only when hover is smaller than middle Y + // if (dragIndex < hoverIndex && hoverActualY < hoverMiddleY) return + // // if dragging up, continue only when hover is bigger than middle Y + // if (dragIndex > hoverIndex && hoverActualY > hoverMiddleY) return + + // moveListItem(dragIndex, hoverIndex) + // item.index = hoverIndex + // }, + }, []) + + console.log('spec', spec) + + const ref: any = useRef(null) + const dragDropRef: any = dragRef(dropRef(ref)) return ( -
+
{/* */} -
+
{widget.name} - {widget.position}
-
- - -
-
- {list && list.map(item => )} +
+
+ + +
+
+ Right +
+
) } diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx new file mode 100644 index 000000000..1b1256793 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useDashboardStore } from '../../store/store'; +import WidgetWrapper from '../../WidgetWrapper'; +import { NoContent, Button, Loader } from 'UI'; +import { useObserver } from 'mobx-react-lite'; +// import { divider } from '../../Filters/filters.css'; + +function DashboardWidgetGrid(props) { + const store: any = useDashboardStore(); + const loading = store.isLoading; + const dashbaord = store.selectedDashboard; + const list = dashbaord.widgets; + return useObserver(() => ( + + +

Metrics helps you visualize trends from sessions captured by OpenReplay

+ +
+ } + > +
+ {list && list.map((item, index) => ( + dashbaord.swapWidgetPosition(dragIndex, hoverIndex)} + /> + ))} +
+ + + )); +} + +export default DashboardWidgetGrid; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/index.ts b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/index.ts new file mode 100644 index 000000000..410933285 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardWidgetGrid'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index c25b2546c..ba6df6ce4 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -1,20 +1,24 @@ import { useObserver } from 'mobx-react-lite'; import React from 'react'; -import { Icon, NoContent, Label, Link } from 'UI'; +import { Icon, NoContent, Label, Link, Pagination } from 'UI'; import { useDashboardStore } from '../../store/store'; +import { getRE } from 'App/utils'; interface Props { } function MetricsList(props: Props) { const store: any = useDashboardStore(); const widgets = store.widgets; const lenth = widgets.length; - const currentPage = store.metricsPage; - const totalPages = widgets.length; + const currentPage = useObserver(() => store.metricsPage); + const metricsSearch = useObserver(() => store.metricsSearch); + + const filterRE = getRE(metricsSearch, 'i'); + const list = widgets.filter(w => filterRE.test(w.name)) + const totalPages = list.length; const pageSize = store.metricsPageSize; const start = (currentPage - 1) * pageSize; const end = currentPage * pageSize; - const list = widgets.slice(start, end); return useObserver(() => ( @@ -28,7 +32,7 @@ function MetricsList(props: Props) {
Last Modified
- {list.map((metric: any) => ( + {list.slice(start, end).map((metric: any) => (
@@ -55,6 +59,16 @@ function MetricsList(props: Props) {
))}
+ +
+ store.updateKey('metricsPage', page)} + limit={pageSize} + debounceRequest={100} + /> +
)); } diff --git a/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx new file mode 100644 index 000000000..45c209350 --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx @@ -0,0 +1,25 @@ +import { useObserver } from 'mobx-react-lite'; +import React from 'react'; +import { useDashboardStore } from '../../store/store'; +import { Icon } from 'UI'; + +function MetricsSearch(props) { + const store: any = useDashboardStore(); + const metricsSearch = useObserver(() => store.metricsSearch); + + + return useObserver(() => ( +
+ + store.updateKey(name, value)} + /> +
+ )); +} + +export default MetricsSearch; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsSearch/index.ts b/frontend/app/components/Dashboard/components/MetricsSearch/index.ts new file mode 100644 index 000000000..cf23f645d --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricsSearch/index.ts @@ -0,0 +1 @@ +export { default } from './MetricsSearch'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx index e00402d1f..b8ec01d25 100644 --- a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Button, PageTitle, Icon, Link } from 'UI'; import { withSiteId, dashboardMetricCreate } from 'App/routes'; import MetricsList from '../MetricsList'; +import MetricsSearch from '../MetricsSearch'; function MetricsView(props) { return ( @@ -9,11 +10,8 @@ function MetricsView(props) {
{/* */} -
- +
+
diff --git a/frontend/app/components/Dashboard/store/dashboard.ts b/frontend/app/components/Dashboard/store/dashboard.ts index c8f4a498b..dbc6d221f 100644 --- a/frontend/app/components/Dashboard/store/dashboard.ts +++ b/frontend/app/components/Dashboard/store/dashboard.ts @@ -104,6 +104,7 @@ export default class Dashboard { } swapWidgetPosition(positionA, positionB) { + console.log('swapWidgetPosition', positionA, positionB) const widgetA = this.widgets[positionA] const widgetB = this.widgets[positionB] this.widgets[positionA] = widgetB diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 44b33f2f7..8373c3fe9 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -14,15 +14,12 @@ export default class DashboardStore { widgets: Widget[] = [] metricsPage: number = 1 metricsPageSize: number = 10 + metricsSearch: string = '' private client = new APIClient() constructor() { makeAutoObservable(this, { - dashboards: observable, - selectedDashboard: observable, - isLoading: observable, - resetCurrentWidget: action, addDashboard: action, removeDashboard: action, @@ -38,6 +35,7 @@ export default class DashboardStore { fromJson: action, setSiteId: action, editWidget: action, + updateKey: action, }) @@ -54,7 +52,7 @@ export default class DashboardStore { // this.selectedDashboard?.swapWidgetPosition(2, 0) // }, 3000) - for (let i = 0; i < 20; i++) { + for (let i = 0; i < 15; i++) { const widget: any= {}; widget.widgetId = `${i}` widget.name = `Widget ${i}`; @@ -84,6 +82,10 @@ export default class DashboardStore { } + updateKey(key: any, value: any) { + this[key] = value + } + resetCurrentWidget() { this.currentWidget = new Widget() } diff --git a/frontend/app/components/hocs/dnd.js b/frontend/app/components/hocs/dnd.js index dd2a4b784..3e4f42e8e 100644 --- a/frontend/app/components/hocs/dnd.js +++ b/frontend/app/components/hocs/dnd.js @@ -1,6 +1,6 @@ -import HTML5Backend from 'react-dnd-html5-backend'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { findDOMNode } from 'react-dom'; -import { DragSource, DropTarget, DragDropContext } from 'react-dnd'; +import { DragSource, DropTarget, DndContext } from 'react-dnd'; const cardSource = { beginDrag(props) { @@ -50,7 +50,7 @@ const cardTarget = { }, }; -export const DNDContext = DragDropContext(HTML5Backend); +export const DNDContext = DndContext(HTML5Backend); export const DNDSource = name => DragSource(name, cardSource, (connect, monitor) => ({ connectDragSource: connect.dragSource(), diff --git a/frontend/app/components/shared/EventFilter/EventEditor.js b/frontend/app/components/shared/EventFilter/EventEditor.js index f2d5dee8f..4fe2c5ee8 100644 --- a/frontend/app/components/shared/EventFilter/EventEditor.js +++ b/frontend/app/components/shared/EventFilter/EventEditor.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { DNDSource, DNDTarget } from 'Components/hocs/dnd'; +// import { DNDSource, DNDTarget } from 'Components/hocs/dnd'; import Event, { TYPES } from 'Types/filter/event'; import { operatorOptions } from 'Types/filter'; import { editEvent, removeEvent, clearEvents, applyFilter } from 'Duck/funnelFilters'; @@ -24,8 +24,8 @@ const getLabel = ({ type }) => { return getPlaceholder({ type }); }; -@DNDTarget('event') -@DNDSource('event') +// @DNDTarget('event') +// @DNDSource('event') @connect(state => ({ isLastEvent: state.getIn([ 'filters', 'appliedFilter', 'events' ]).size === 1, funnel: state.getIn(['funnels', 'instance']), diff --git a/frontend/app/components/shared/EventFilter/EventFilter.js b/frontend/app/components/shared/EventFilter/EventFilter.js index 9a852398e..ca3db8fc5 100644 --- a/frontend/app/components/shared/EventFilter/EventFilter.js +++ b/frontend/app/components/shared/EventFilter/EventFilter.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { DNDContext } from 'Components/hocs/dnd'; +// import { DNDContext } from 'Components/hocs/dnd'; import { addEvent, applyFilter, moveEvent, clearEvents, addCustomFilter, addAttribute, setSearchQuery, setActiveFlow, setFilterOption @@ -39,7 +39,7 @@ import CustomFilters from './CustomFilters'; updateFunnelFilters, refreshFunnel }) -@DNDContext +// @DNDContext export default class EventFilter extends React.PureComponent { state = { search: '', showFilterModal: false, showPlacehoder: true, showSaveModal: false } fetchEventList = debounce(this.props.fetchEventList, 500) diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index 6460a9eff..629ea33ad 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -9,6 +9,9 @@ import DashboardStore from './components/Dashboard/store'; import { DashboardStoreProvider } from './components/Dashboard/store/store'; import { ModalProvider } from './components/Modal/ModalContext'; import ModalRoot from './components/Modal/ModalRoot'; +import { HTML5Backend } from 'react-dnd-html5-backend' +import { DndProvider } from 'react-dnd' + document.addEventListener('DOMContentLoaded', () => { @@ -16,12 +19,14 @@ document.addEventListener('DOMContentLoaded', () => { render( ( - - - - - - + + + + + + + + ), document.getElementById('app'), From 65b2b037e6e40276e8fcedd37a20757ad5df4bb9 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 29 Mar 2022 12:11:21 +0200 Subject: [PATCH 21/87] feat(ui) - dashboards - updated react-dnd version --- frontend/package-lock.json | 27818 +++++++++++++++++++++++++++++++++-- frontend/package.json | 4 +- 2 files changed, 26936 insertions(+), 886 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cd3489c80..5393d1fcc 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,15 +1,26088 @@ { "name": "openreplay", "version": "1.3.6", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, - "dependencies": { - "@babel/cli": { + "packages": { + "": { + "name": "openreplay", + "version": "1.3.6", + "dependencies": { + "@sentry/browser": "^5.21.1", + "classnames": "^2.2.6", + "codemirror": "^5.62.3", + "copy-to-clipboard": "^3.3.1", + "datamaps": "^0.5.9", + "deep-diff": "^1.0.2", + "immutable": "^4.0.0-rc.12", + "jsbi": "^4.1.0", + "jshint": "^2.11.1", + "luxon": "^1.24.1", + "mobx": "^6.3.8", + "mobx-react-lite": "^3.1.6", + "moment": "^2.27.0", + "moment-range": "^4.0.2", + "peerjs": "^1.3.2", + "rc-time-picker": "^3.7.3", + "react": "^16.13.1", + "react-circular-progressbar": "^2.0.3", + "react-codemirror2": "^5.1.0", + "react-confirm": "^0.1.20", + "react-datepicker": "^2.16.0", + "react-daterange-picker": "^2.0.1", + "react-dnd": "^15.1.1", + "react-dnd-html5-backend": "^15.1.2", + "react-dom": "^16.13.1", + "react-draggable": "^4.4.4", + "react-google-recaptcha": "^1.1.0", + "react-highlight": "^0.14.0", + "react-json-view": "^1.19.1", + "react-lazyload": "^3.0.0", + "react-redux": "^5.1.2", + "react-router": "^4.3.1", + "react-router-dom": "^4.3.1", + "react-tippy": "^1.4.0", + "react-toastify": "^5.5.0", + "react-virtualized": "^9.22.2", + "recharts": "^1.8.5", + "redux": "^4.0.5", + "redux-immutable": "^4.0.0", + "redux-thunk": "^2.3.0", + "semantic-ui-react": "^0.87.3", + "socket.io-client": "^4.4.1", + "source-map": "^0.7.3", + "syncod": "^0.0.1", + "tailwindcss": "^1.5.2" + }, + "devDependencies": { + "@babel/cli": "^7.10.5", + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-decorators": "^7.10.5", + "@babel/plugin-proposal-private-methods": "^7.10.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/preset-env": "^7.10.4", + "@babel/preset-flow": "^7.10.4", + "@babel/preset-react": "^7.10.4", + "@openreplay/sourcemap-uploader": "^3.0.0", + "@storybook/react": "^6.4.8", + "autoprefixer": "^7.2.5", + "babel-loader": "^8.1.0", + "babel-plugin-recharts": "^1.2.1", + "circular-dependency-plugin": "^5.2.0", + "copy-webpack-plugin": "^5.1.1", + "country-data": "0.0.31", + "css-loader": "^3.6.0", + "cssnano": "^5.0.12", + "deasync-promise": "^1.0.1", + "deploy-aws-s3-cloudfront": "^3.6.0", + "dotenv": "^6.2.0", + "eslint-config-airbnb": "^16.1.0", + "eslint-import-resolver-babel-module": "^4.0.0", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-jsx-a11y": "^6.3.1", + "eslint-plugin-react": "^7.20.6", + "faker": "^5.5.3", + "flow-bin": "^0.115.0", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.7.0", + "minio": "^7.0.18", + "moment-locales-webpack-plugin": "^1.2.0", + "postcss-import": "^12.0.1", + "postcss-inline-svg": "^3.1.1", + "postcss-loader": "^3.0.0", + "postcss-mixins": "^6.2.3", + "postcss-nesting": "^4.2.1", + "postcss-simple-vars": "^4.1.0", + "style-loader": "^0.23.1", + "svgo": "^1.3.2", + "ts-loader": "^8.3.0", + "typed-css-modules": "^0.7.0", + "typescript": "^3.9.9", + "webpack": "^4.46.0", + "webpack-bundle-analyzer": "^3.8.0", + "webpack-cli": "^3.3.12", + "webpack-dev-server": "^3.11.0" + }, + "engines": { + "node": ">=10.14.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/cli": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.17.6.tgz", + "integrity": "sha512-l4w608nsDNlxZhiJ5tE3DbNmr61fIKMZ6fTBo171VEFuFMIYuJ3mHRhTLEkKKyvx2Mizkkv/0a8OJOnZqkKYNA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.4", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.0.0", + "make-dir": "^2.1.0", + "slash": "^2.0.0", + "source-map": "^0.5.0" + }, + "bin": { + "babel": "bin/babel.js", + "babel-external-helpers": "bin/babel-external-helpers.js" + }, + "engines": { + "node": ">=6.9.0" + }, + "optionalDependencies": { + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/cli/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", + "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", + "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz", + "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz", + "integrity": "sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", + "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "regexpu-core": "^5.0.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz", + "integrity": "sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", + "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz", + "integrity": "sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", + "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", + "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", + "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz", + "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz", + "integrity": "sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz", + "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.16.8.tgz", - "integrity": "sha512-FTKBbxyk5TclXOGmwYyqelqP5IF6hMxaeJskd85jbR5jBfYlwqgwAbJwnixi1ZBbTqKfFuAA95mdmUFeSRwyJA==", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz", + "integrity": "sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-wrap-function": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz", + "integrity": "sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-member-expression-to-functions": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/traverse": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz", + "integrity": "sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", + "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", + "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz", + "integrity": "sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.16.7", + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.16.8", + "@babel/types": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.16.7", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.16.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", + "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz", + "integrity": "sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz", + "integrity": "sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz", + "integrity": "sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz", + "integrity": "sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.8.tgz", + "integrity": "sha512-U69odN4Umyyx1xO1rTII0IDkAEC+RNlcKXtqOblfpzqy1C+aOplb76BQNq0+XdpVkOaPlpEDwd++joY8FNFJKA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/plugin-syntax-decorators": "^7.17.0", + "charcodes": "^0.2.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz", + "integrity": "sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-default-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.16.7.tgz", + "integrity": "sha512-+cENpW1rgIjExn+o5c8Jw/4BuH4eGKKYvkMB8/0ZxFQ9mC0t4z09VsPIwNg6waF69QYC81zxGeAsREGuqQoKeg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-default-from": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz", + "integrity": "sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz", + "integrity": "sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz", + "integrity": "sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz", + "integrity": "sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz", + "integrity": "sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.0", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz", + "integrity": "sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz", + "integrity": "sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz", + "integrity": "sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.10", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz", + "integrity": "sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz", + "integrity": "sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.0.tgz", + "integrity": "sha512-qWe85yCXsvDEluNP0OyeQjH63DlhAR3W7K9BxxU1MvbDb48tgBG+Ao6IJJ6smPDrrVzSQZrbF6donpkFBMcs3A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-default-from": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.16.7.tgz", + "integrity": "sha512-4C3E4NsrLOgftKaTYTULhHsuQrGv3FHrBzOMDiS7UYKIpgGBkAdawg4h+EI8zPeK9M0fiIIh72hIwsI24K7MbA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz", + "integrity": "sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz", + "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz", + "integrity": "sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz", + "integrity": "sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz", + "integrity": "sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-remap-async-to-generator": "^7.16.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz", + "integrity": "sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz", + "integrity": "sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz", + "integrity": "sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-optimise-call-expression": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz", + "integrity": "sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz", + "integrity": "sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz", + "integrity": "sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz", + "integrity": "sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz", + "integrity": "sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-flow": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz", + "integrity": "sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz", + "integrity": "sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz", + "integrity": "sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz", + "integrity": "sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz", + "integrity": "sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz", + "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-identifier": "^7.16.7", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz", + "integrity": "sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz", + "integrity": "sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz", + "integrity": "sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz", + "integrity": "sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-replace-supers": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz", + "integrity": "sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz", + "integrity": "sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz", + "integrity": "sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz", + "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.16.7", + "@babel/types": "^7.17.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz", + "integrity": "sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz", + "integrity": "sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz", + "integrity": "sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==", + "dev": true, + "dependencies": { + "regenerator-transform": "^0.14.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz", + "integrity": "sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", + "integrity": "sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", + "integrity": "sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz", + "integrity": "sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz", + "integrity": "sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz", + "integrity": "sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.16.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz", + "integrity": "sha512-bHdQ9k7YpBDO2d0NVfkj51DpQcvwIzIusJ7mEUaMlbZq3Kt/U47j24inXZHQ5MDiYpCs+oZiwnXyKedE8+q7AQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/plugin-syntax-typescript": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz", + "integrity": "sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz", + "integrity": "sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.16.11", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.16.11.tgz", + "integrity": "sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.16.8", + "@babel/helper-compilation-targets": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-async-generator-functions": "^7.16.8", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-class-static-block": "^7.16.7", + "@babel/plugin-proposal-dynamic-import": "^7.16.7", + "@babel/plugin-proposal-export-namespace-from": "^7.16.7", + "@babel/plugin-proposal-json-strings": "^7.16.7", + "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7", + "@babel/plugin-proposal-numeric-separator": "^7.16.7", + "@babel/plugin-proposal-object-rest-spread": "^7.16.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", + "@babel/plugin-proposal-optional-chaining": "^7.16.7", + "@babel/plugin-proposal-private-methods": "^7.16.11", + "@babel/plugin-proposal-private-property-in-object": "^7.16.7", + "@babel/plugin-proposal-unicode-property-regex": "^7.16.7", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.16.7", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-block-scoped-functions": "^7.16.7", + "@babel/plugin-transform-block-scoping": "^7.16.7", + "@babel/plugin-transform-classes": "^7.16.7", + "@babel/plugin-transform-computed-properties": "^7.16.7", + "@babel/plugin-transform-destructuring": "^7.16.7", + "@babel/plugin-transform-dotall-regex": "^7.16.7", + "@babel/plugin-transform-duplicate-keys": "^7.16.7", + "@babel/plugin-transform-exponentiation-operator": "^7.16.7", + "@babel/plugin-transform-for-of": "^7.16.7", + "@babel/plugin-transform-function-name": "^7.16.7", + "@babel/plugin-transform-literals": "^7.16.7", + "@babel/plugin-transform-member-expression-literals": "^7.16.7", + "@babel/plugin-transform-modules-amd": "^7.16.7", + "@babel/plugin-transform-modules-commonjs": "^7.16.8", + "@babel/plugin-transform-modules-systemjs": "^7.16.7", + "@babel/plugin-transform-modules-umd": "^7.16.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8", + "@babel/plugin-transform-new-target": "^7.16.7", + "@babel/plugin-transform-object-super": "^7.16.7", + "@babel/plugin-transform-parameters": "^7.16.7", + "@babel/plugin-transform-property-literals": "^7.16.7", + "@babel/plugin-transform-regenerator": "^7.16.7", + "@babel/plugin-transform-reserved-words": "^7.16.7", + "@babel/plugin-transform-shorthand-properties": "^7.16.7", + "@babel/plugin-transform-spread": "^7.16.7", + "@babel/plugin-transform-sticky-regex": "^7.16.7", + "@babel/plugin-transform-template-literals": "^7.16.7", + "@babel/plugin-transform-typeof-symbol": "^7.16.7", + "@babel/plugin-transform-unicode-escapes": "^7.16.7", + "@babel/plugin-transform-unicode-regex": "^7.16.7", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.16.8", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "core-js-compat": "^3.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-flow": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.16.7.tgz", + "integrity": "sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-transform-flow-strip-types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.16.7.tgz", + "integrity": "sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-transform-react-display-name": "^7.16.7", + "@babel/plugin-transform-react-jsx": "^7.16.7", + "@babel/plugin-transform-react-jsx-development": "^7.16.7", + "@babel/plugin-transform-react-pure-annotations": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz", + "integrity": "sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.16.7", + "@babel/helper-validator-option": "^7.16.7", + "@babel/plugin-transform-typescript": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/register": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.17.7.tgz", + "integrity": "sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.5", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz", + "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==", + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime-corejs3": { + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.8.tgz", + "integrity": "sha512-ZbYSUvoSF6dXZmMl/CYTMOvzIFnbGfv4W3SEHYgMvNsFTeLaF2gkGAF4K2ddmtSK4Emej+0aYcnSC6N5dPCXUQ==", + "dev": true, + "dependencies": { + "core-js-pure": "^3.20.2", + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.16.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", + "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/parser": "^7.16.7", + "@babel/types": "^7.16.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.16.7", + "@babel/generator": "^7.17.3", + "@babel/helper-environment-visitor": "^7.16.7", + "@babel/helper-function-name": "^7.16.7", + "@babel/helper-hoist-variables": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@emotion/cache": { + "version": "10.0.29", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz", + "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==", + "dev": true, + "dependencies": { + "@emotion/sheet": "0.9.4", + "@emotion/stylis": "0.8.5", + "@emotion/utils": "0.11.3", + "@emotion/weak-memoize": "0.2.5" + } + }, + "node_modules/@emotion/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz", + "integrity": "sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "@emotion/cache": "^10.0.27", + "@emotion/css": "^10.0.27", + "@emotion/serialize": "^0.11.15", + "@emotion/sheet": "0.9.4", + "@emotion/utils": "0.11.3" + }, + "peerDependencies": { + "react": ">=16.3.0" + } + }, + "node_modules/@emotion/css": { + "version": "10.0.27", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz", + "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==", + "dev": true, + "dependencies": { + "@emotion/serialize": "^0.11.15", + "@emotion/utils": "0.11.3", + "babel-plugin-emotion": "^10.0.27" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "dev": true + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "dev": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "dev": true + }, + "node_modules/@emotion/serialize": { + "version": "0.11.16", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz", + "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==", + "dev": true, + "dependencies": { + "@emotion/hash": "0.8.0", + "@emotion/memoize": "0.7.4", + "@emotion/unitless": "0.7.5", + "@emotion/utils": "0.11.3", + "csstype": "^2.5.7" + } + }, + "node_modules/@emotion/sheet": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz", + "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==", + "dev": true + }, + "node_modules/@emotion/styled": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-10.3.0.tgz", + "integrity": "sha512-GgcUpXBBEU5ido+/p/mCT2/Xx+Oqmp9JzQRuC+a4lYM4i4LBBn/dWvc0rQ19N9ObA8/T4NWMrPNe79kMBDJqoQ==", + "dev": true, + "dependencies": { + "@emotion/styled-base": "^10.3.0", + "babel-plugin-emotion": "^10.0.27" + }, + "peerDependencies": { + "@emotion/core": "^10.0.27", + "react": ">=16.3.0" + } + }, + "node_modules/@emotion/styled-base": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.3.0.tgz", + "integrity": "sha512-PBRqsVKR7QRNkmfH78hTSSwHWcwDpecH9W6heujWAcyp2wdz/64PP73s7fWS1dIPm8/Exc8JAzYS8dEWXjv60w==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "@emotion/is-prop-valid": "0.8.8", + "@emotion/serialize": "^0.11.15", + "@emotion/utils": "0.11.3" + }, + "peerDependencies": { + "@emotion/core": "^10.0.28", + "react": ">=16.3.0" + } + }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "dev": true + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "dev": true + }, + "node_modules/@emotion/utils": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz", + "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==", + "dev": true + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==", + "dev": true + }, + "node_modules/@fullhuman/postcss-purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz", + "integrity": "sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw==", + "dependencies": { + "postcss": "7.0.32", + "purgecss": "^2.3.0" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@fullhuman/postcss-purgecss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "node_modules/@hypnosphi/create-react-context": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz", + "integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==", + "dependencies": { + "gud": "^1.0.0", + "warning": "^4.0.3" + }, + "peerDependencies": { + "prop-types": "^15.0.0", + "react": ">=0.14.0" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@mdx-js/mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", + "integrity": "sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==", + "dev": true, + "dependencies": { + "@babel/core": "7.12.9", + "@babel/plugin-syntax-jsx": "7.12.1", + "@babel/plugin-syntax-object-rest-spread": "7.8.3", + "@mdx-js/util": "1.6.22", + "babel-plugin-apply-mdx-type-prop": "1.6.22", + "babel-plugin-extract-import-names": "1.6.22", + "camelcase-css": "2.0.1", + "detab": "2.0.4", + "hast-util-raw": "6.0.1", + "lodash.uniq": "4.5.0", + "mdast-util-to-hast": "10.0.1", + "remark-footnotes": "2.0.0", + "remark-mdx": "1.6.22", + "remark-parse": "8.0.3", + "remark-squeeze-paragraphs": "4.0.0", + "style-to-object": "0.3.0", + "unified": "9.2.0", + "unist-builder": "2.0.3", + "unist-util-visit": "2.0.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@mdx-js/mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@mdx-js/mdx/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@mdx-js/mdx/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@mdx-js/util": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/util/-/util-1.6.22.tgz", + "integrity": "sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "dependencies": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@mrmlnc/readdir-enhanced/node_modules/glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "node_modules/@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, + "optional": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/fs/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/fs/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@openreplay/sourcemap-uploader": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@openreplay/sourcemap-uploader/-/sourcemap-uploader-3.0.6.tgz", + "integrity": "sha512-zoGduDViFCbbGiNO4E+OSJoIQlCWE8FvRVqIwISs3GXxDHuuMPlP3ivZwo5mzSQb6C/8uKqJPQ2n2LmR2/Ow1w==", + "dev": true, + "dependencies": { + "argparse": "^1.0.10", + "glob-promise": "^3.4.0" + }, + "bin": { + "sourcemap-uploader": "cli.js" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.4.tgz", + "integrity": "sha512-zZbZeHQDnoTlt2AF+diQT0wsSXpvWiaIOZwBRdltNFhG1+I3ozyaw7U/nBiUwyJ0D+zwdXp0E3bWOl38Ag2BMw==", + "dev": true, + "dependencies": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.8.1", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <3.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.4.tgz", + "integrity": "sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-dnd/asap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.0.tgz", + "integrity": "sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==" + }, + "node_modules/@react-dnd/invariant": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-3.0.0.tgz", + "integrity": "sha512-keberJRIqPX15IK3SWS/iO1t/kGETiL1oczKrDitAaMnQ+kpHf81l3MrRmFjvfqcnApE+izEvwM6GsyoIcpsVA==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-3.0.0.tgz", + "integrity": "sha512-1ELWQdJB2UrCXTKK5cCD9uGLLIwECLIEdttKA255owdpchtXohIjZBTlFJszwYi2ZKe2Do+QvUzsGyGCMNwbdw==" + }, + "node_modules/@semantic-ui-react/event-stack": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@semantic-ui-react/event-stack/-/event-stack-3.1.2.tgz", + "integrity": "sha512-Yd0Qf7lPCIjzJ9bZYfurlNu2RDXT6KKSyubHfYK3WjRauhxCsq6Fk2LMRI9DEvShoEU+AsLSv3NGkqXAcVp0zg==", + "dependencies": { + "exenv": "^1.2.2", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0", + "react-dom": "^16.0.0 || ^17.0.0" + } + }, + "node_modules/@sentry/browser": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.30.0.tgz", + "integrity": "sha512-rOb58ZNVJWh1VuMuBG1mL9r54nZqKeaIlwSlvzJfc89vyfd7n6tQ1UXMN383QBz/MS5H5z44Hy5eE+7pCrYAfw==", + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@socket.io/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz", + "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==" + }, + "node_modules/@storybook/addons": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.19.tgz", + "integrity": "sha512-QNyRYhpqmHV8oJxxTBdkRlLSbDFhpBvfvMfIrIT1UXb/eemdBZTaCGVvXZ9UixoEEI7f8VwAQ44IvkU5B1509w==", + "dev": true, + "dependencies": { + "@storybook/api": "6.4.19", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "@storybook/router": "6.4.19", + "@storybook/theming": "6.4.19", + "@types/webpack-env": "^1.16.0", + "core-js": "^3.8.2", + "global": "^4.4.0", + "regenerator-runtime": "^0.13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@storybook/api": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.4.19.tgz", + "integrity": "sha512-aDvea+NpQCBjpNp9YidO1Pr7fzzCp15FSdkG+2ihGQfv5raxrN+IIJnGUXecpe71nvlYiB+29UXBVK7AL0j51Q==", + "dev": true, + "dependencies": { + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "@storybook/router": "6.4.19", + "@storybook/semver": "^7.3.2", + "@storybook/theming": "6.4.19", + "core-js": "^3.8.2", + "fast-deep-equal": "^3.1.3", + "global": "^4.4.0", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "regenerator-runtime": "^0.13.7", + "store2": "^2.12.0", + "telejson": "^5.3.2", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@storybook/builder-webpack4": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.4.19.tgz", + "integrity": "sha512-wxA6SMH11duc9D53aeVVBwrVRemFIoxHp/dOugkkg6ZZFAb4ZmWzf/ENc3vQIZdZpfNRi7IZIZEOfoHc994cmw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-decorators": "^7.12.12", + "@babel/plugin-proposal-export-default-from": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.12", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/preset-env": "^7.12.11", + "@babel/preset-react": "^7.12.10", + "@babel/preset-typescript": "^7.12.7", + "@storybook/addons": "6.4.19", + "@storybook/api": "6.4.19", + "@storybook/channel-postmessage": "6.4.19", + "@storybook/channels": "6.4.19", + "@storybook/client-api": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/components": "6.4.19", + "@storybook/core-common": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/node-logger": "6.4.19", + "@storybook/preview-web": "6.4.19", + "@storybook/router": "6.4.19", + "@storybook/semver": "^7.3.2", + "@storybook/store": "6.4.19", + "@storybook/theming": "6.4.19", + "@storybook/ui": "6.4.19", + "@types/node": "^14.0.10", + "@types/webpack": "^4.41.26", + "autoprefixer": "^9.8.6", + "babel-loader": "^8.0.0", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-polyfill-corejs3": "^0.1.0", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "core-js": "^3.8.2", + "css-loader": "^3.6.0", + "file-loader": "^6.2.0", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^4.1.6", + "glob": "^7.1.6", + "glob-promise": "^3.4.0", + "global": "^4.4.0", + "html-webpack-plugin": "^4.0.0", + "pnp-webpack-plugin": "1.6.4", + "postcss": "^7.0.36", + "postcss-flexbugs-fixes": "^4.2.1", + "postcss-loader": "^4.2.0", + "raw-loader": "^4.0.2", + "stable": "^0.1.8", + "style-loader": "^1.3.0", + "terser-webpack-plugin": "^4.2.3", + "ts-dedent": "^2.0.0", + "url-loader": "^4.1.1", + "util-deprecate": "^1.0.2", + "webpack": "4", + "webpack-dev-middleware": "^3.7.3", + "webpack-filter-warnings-plugin": "^1.2.1", + "webpack-hot-middleware": "^2.25.1", + "webpack-virtual-modules": "^0.2.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz", + "integrity": "sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/babel-plugin-macros/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz", + "integrity": "sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.1.5", + "core-js-compat": "^3.8.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/fork-ts-checker-webpack-plugin": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz", + "integrity": "sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.5.5", + "chalk": "^2.4.1", + "micromatch": "^3.1.10", + "minimatch": "^3.0.4", + "semver": "^5.6.0", + "tapable": "^1.0.0", + "worker-rpc": "^0.1.0" + }, + "engines": { + "node": ">=6.11.5", + "yarn": ">=1.0.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/html-webpack-plugin": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz", + "integrity": "sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^5.0.0", + "@types/tapable": "^1.0.5", + "@types/webpack": "^4.41.8", + "html-minifier-terser": "^5.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.20", + "pretty-error": "^2.1.1", + "tapable": "^1.1.3", + "util.promisify": "1.0.0" + }, + "engines": { + "node": ">=6.9" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/html-webpack-plugin/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/@storybook/builder-webpack4/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/@storybook/builder-webpack4/node_modules/postcss-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.3.0.tgz", + "integrity": "sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/postcss-loader/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/@storybook/builder-webpack4/node_modules/style-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", + "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^2.7.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/style-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/builder-webpack4/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@storybook/channel-postmessage": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.4.19.tgz", + "integrity": "sha512-E5h/itFzQ/6M08LR4kqlgqqmeO3tmavI+nUAlZrkCrotpJFNMHE2i0PQHg0TkFJrRDpYcrwD+AjUW4IwdqrisQ==", + "dev": true, + "dependencies": { + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", + "core-js": "^3.8.2", + "global": "^4.4.0", + "qs": "^6.10.0", + "telejson": "^5.3.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/channel-websocket": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-6.4.19.tgz", + "integrity": "sha512-cXKwQjIXttfdUyZlcHORelUmJ5nUKswsnCA/qy7IRWpZjD8yQJcNk1dYC+tTHDVqFgdRT89pL0hRRB1rlaaR8Q==", + "dev": true, + "dependencies": { + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "core-js": "^3.8.2", + "global": "^4.4.0", + "telejson": "^5.3.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/channels": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.19.tgz", + "integrity": "sha512-EwyoncFvTfmIlfsy8jTfayCxo2XchPkZk/9txipugWSmc057HdklMKPLOHWP0z5hLH0IbVIKXzdNISABm36jwQ==", + "dev": true, + "dependencies": { + "core-js": "^3.8.2", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/client-api": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.4.19.tgz", + "integrity": "sha512-OCrT5Um3FDvZnimQKwWtwsaI+5agPwq2i8YiqlofrI/NPMKp0I7DEkCGwE5IRD1Q8BIKqHcMo5tTmfYi0AxyOg==", + "dev": true, + "dependencies": { + "@storybook/addons": "6.4.19", + "@storybook/channel-postmessage": "6.4.19", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "@storybook/store": "6.4.19", + "@types/qs": "^6.9.5", + "@types/webpack-env": "^1.16.0", + "core-js": "^3.8.2", + "fast-deep-equal": "^3.1.3", + "global": "^4.4.0", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "regenerator-runtime": "^0.13.7", + "store2": "^2.12.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@storybook/client-logger": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.4.19.tgz", + "integrity": "sha512-zmg/2wyc9W3uZrvxaW4BfHcr40J0v7AGslqYXk9H+ERLVwIvrR4NhxQFaS6uITjBENyRDxwzfU3Va634WcmdDQ==", + "dev": true, + "dependencies": { + "core-js": "^3.8.2", + "global": "^4.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/components": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.4.19.tgz", + "integrity": "sha512-q/0V37YAJA7CNc+wSiiefeM9+3XVk8ixBNylY36QCGJgIeGQ5/79vPyUe6K4lLmsQwpmZsIq1s1Ad5+VbboeOA==", + "dev": true, + "dependencies": { + "@popperjs/core": "^2.6.0", + "@storybook/client-logger": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "@storybook/theming": "6.4.19", + "@types/color-convert": "^2.0.0", + "@types/overlayscrollbars": "^1.12.0", + "@types/react-syntax-highlighter": "11.0.5", + "color-convert": "^2.0.1", + "core-js": "^3.8.2", + "fast-deep-equal": "^3.1.3", + "global": "^4.4.0", + "lodash": "^4.17.21", + "markdown-to-jsx": "^7.1.3", + "memoizerific": "^1.11.3", + "overlayscrollbars": "^1.13.1", + "polished": "^4.0.5", + "prop-types": "^15.7.2", + "react-colorful": "^5.1.2", + "react-popper-tooltip": "^3.1.1", + "react-syntax-highlighter": "^13.5.3", + "react-textarea-autosize": "^8.3.0", + "regenerator-runtime": "^0.13.7", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@storybook/core": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.4.19.tgz", + "integrity": "sha512-55LOQ/h/kf1jMhjN85t/pIEdIwWEG9yV7bdwv3niVvmoypCxyyjn9/QNK0RKYAeDSUtdm6FVoJ6k5CpxWz2d8w==", + "dev": true, + "dependencies": { + "@storybook/core-client": "6.4.19", + "@storybook/core-server": "6.4.19" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@storybook/builder-webpack5": "6.4.19", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@storybook/builder-webpack5": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/core-client": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-6.4.19.tgz", + "integrity": "sha512-rQHRZjhArPleE7/S8ZUolgzwY+hC0smSKX/3PQxO2GcebDjnJj6+iSV3h+aSMHMmTdoCQvjYw9aBpT8scuRe+A==", + "dev": true, + "dependencies": { + "@storybook/addons": "6.4.19", + "@storybook/channel-postmessage": "6.4.19", + "@storybook/channel-websocket": "6.4.19", + "@storybook/client-api": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "@storybook/preview-web": "6.4.19", + "@storybook/store": "6.4.19", + "@storybook/ui": "6.4.19", + "airbnb-js-shims": "^2.2.1", + "ansi-to-html": "^0.6.11", + "core-js": "^3.8.2", + "global": "^4.4.0", + "lodash": "^4.17.21", + "qs": "^6.10.0", + "regenerator-runtime": "^0.13.7", + "ts-dedent": "^2.0.0", + "unfetch": "^4.2.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0", + "webpack": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/core-common": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-6.4.19.tgz", + "integrity": "sha512-X1pJJkO48DFxl6iyEemIKqRkJ7j9/cBh3BRBUr+xZHXBvnD0GKDXIocwh0PjSxSC6XSu3UCQnqtKi3PbjRl8Dg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-decorators": "^7.12.12", + "@babel/plugin-proposal-export-default-from": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.12.1", + "@babel/plugin-transform-block-scoping": "^7.12.12", + "@babel/plugin-transform-classes": "^7.12.1", + "@babel/plugin-transform-destructuring": "^7.12.1", + "@babel/plugin-transform-for-of": "^7.12.1", + "@babel/plugin-transform-parameters": "^7.12.1", + "@babel/plugin-transform-shorthand-properties": "^7.12.1", + "@babel/plugin-transform-spread": "^7.12.1", + "@babel/preset-env": "^7.12.11", + "@babel/preset-react": "^7.12.10", + "@babel/preset-typescript": "^7.12.7", + "@babel/register": "^7.12.1", + "@storybook/node-logger": "6.4.19", + "@storybook/semver": "^7.3.2", + "@types/node": "^14.0.10", + "@types/pretty-hrtime": "^1.0.0", + "babel-loader": "^8.0.0", + "babel-plugin-macros": "^3.0.1", + "babel-plugin-polyfill-corejs3": "^0.1.0", + "chalk": "^4.1.0", + "core-js": "^3.8.2", + "express": "^4.17.1", + "file-system-cache": "^1.0.5", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.0.4", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "handlebars": "^4.7.7", + "interpret": "^2.2.0", + "json5": "^2.1.3", + "lazy-universal-dotenv": "^3.0.1", + "picomatch": "^2.3.0", + "pkg-dir": "^5.0.0", + "pretty-hrtime": "^1.0.3", + "resolve-from": "^5.0.0", + "slash": "^3.0.0", + "telejson": "^5.3.2", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2", + "webpack": "4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/core-common/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz", + "integrity": "sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@storybook/core-common/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@storybook/core-common/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz", + "integrity": "sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.1.5", + "core-js-compat": "^3.8.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@storybook/core-common/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@storybook/core-common/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/core-common/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/core-common/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/core-events": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.4.19.tgz", + "integrity": "sha512-KICzUw6XVQUJzFSCXfvhfHAuyhn4Q5J4IZEfuZkcGJS4ODkrO6tmpdYE5Cfr+so95Nfp0ErWiLUuodBsW9/rtA==", + "dev": true, + "dependencies": { + "core-js": "^3.8.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/core-server": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.4.19.tgz", + "integrity": "sha512-bKsUB9f7hl5ya2JXxpIrErmbDQjoH39FVbzYZWjMo4t/b7+Xyi6vYadwyWcqlpUQmis09ZaSMv8L/Tw0TuwLAA==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.3", + "@storybook/builder-webpack4": "6.4.19", + "@storybook/core-client": "6.4.19", + "@storybook/core-common": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "@storybook/csf-tools": "6.4.19", + "@storybook/manager-webpack4": "6.4.19", + "@storybook/node-logger": "6.4.19", + "@storybook/semver": "^7.3.2", + "@storybook/store": "6.4.19", + "@types/node": "^14.0.10", + "@types/node-fetch": "^2.5.7", + "@types/pretty-hrtime": "^1.0.0", + "@types/webpack": "^4.41.26", + "better-opn": "^2.1.1", + "boxen": "^5.1.2", + "chalk": "^4.1.0", + "cli-table3": "^0.6.1", + "commander": "^6.2.1", + "compression": "^1.7.4", + "core-js": "^3.8.2", + "cpy": "^8.1.2", + "detect-port": "^1.3.0", + "express": "^4.17.1", + "file-system-cache": "^1.0.5", + "fs-extra": "^9.0.1", + "globby": "^11.0.2", + "ip": "^1.1.5", + "lodash": "^4.17.21", + "node-fetch": "^2.6.1", + "pretty-hrtime": "^1.0.3", + "prompts": "^2.4.0", + "regenerator-runtime": "^0.13.7", + "serve-favicon": "^2.5.0", + "slash": "^3.0.0", + "telejson": "^5.3.3", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2", + "watchpack": "^2.2.0", + "webpack": "4", + "ws": "^8.2.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@storybook/builder-webpack5": "6.4.19", + "@storybook/manager-webpack5": "6.4.19", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@storybook/builder-webpack5": { + "optional": true + }, + "@storybook/manager-webpack5": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/core-server/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@storybook/core-server/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@storybook/core-server/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@storybook/core-server/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/core-server/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/core-server/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/csf": { + "version": "0.0.2--canary.87bc651.0", + "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.2--canary.87bc651.0.tgz", + "integrity": "sha512-ajk1Uxa+rBpFQHKrCcTmJyQBXZ5slfwHVEaKlkuFaW77it8RgbPJp/ccna3sgoi8oZ7FkkOyvv1Ve4SmwFqRqw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + } + }, + "node_modules/@storybook/csf-tools": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-6.4.19.tgz", + "integrity": "sha512-gf/zRhGoAVsFwSyV2tc+jeJfZQkxF6QsaZgbUSe24/IUvGFCT/PS/jZq1qy7dECAwrTOfykgu8juyBtj6WhWyw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.10", + "@babel/generator": "^7.12.11", + "@babel/parser": "^7.12.11", + "@babel/plugin-transform-react-jsx": "^7.12.12", + "@babel/preset-env": "^7.12.11", + "@babel/traverse": "^7.12.11", + "@babel/types": "^7.12.11", + "@mdx-js/mdx": "^1.6.22", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "core-js": "^3.8.2", + "fs-extra": "^9.0.1", + "global": "^4.4.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.21", + "prettier": ">=2.2.1 <=2.3.0", + "regenerator-runtime": "^0.13.7", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/manager-webpack4": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/manager-webpack4/-/manager-webpack4-6.4.19.tgz", + "integrity": "sha512-R8ugZjTYqXvlc6gDOcw909L65sIleOmIJLZR+N6/H85MivGXHu39jOwONqB7tVACufRty4FNecn8tEiQL2SAKA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-transform-template-literals": "^7.12.1", + "@babel/preset-react": "^7.12.10", + "@storybook/addons": "6.4.19", + "@storybook/core-client": "6.4.19", + "@storybook/core-common": "6.4.19", + "@storybook/node-logger": "6.4.19", + "@storybook/theming": "6.4.19", + "@storybook/ui": "6.4.19", + "@types/node": "^14.0.10", + "@types/webpack": "^4.41.26", + "babel-loader": "^8.0.0", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "chalk": "^4.1.0", + "core-js": "^3.8.2", + "css-loader": "^3.6.0", + "express": "^4.17.1", + "file-loader": "^6.2.0", + "file-system-cache": "^1.0.5", + "find-up": "^5.0.0", + "fs-extra": "^9.0.1", + "html-webpack-plugin": "^4.0.0", + "node-fetch": "^2.6.1", + "pnp-webpack-plugin": "1.6.4", + "read-pkg-up": "^7.0.1", + "regenerator-runtime": "^0.13.7", + "resolve-from": "^5.0.0", + "style-loader": "^1.3.0", + "telejson": "^5.3.2", + "terser-webpack-plugin": "^4.2.3", + "ts-dedent": "^2.0.0", + "url-loader": "^4.1.1", + "util-deprecate": "^1.0.2", + "webpack": "4", + "webpack-dev-middleware": "^3.7.3", + "webpack-virtual-modules": "^0.2.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/html-webpack-plugin": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz", + "integrity": "sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==", + "dev": true, + "dependencies": { + "@types/html-minifier-terser": "^5.0.0", + "@types/tapable": "^1.0.5", + "@types/webpack": "^4.41.8", + "html-minifier-terser": "^5.0.1", + "loader-utils": "^1.2.3", + "lodash": "^4.17.20", + "pretty-error": "^2.1.1", + "tapable": "^1.1.3", + "util.promisify": "1.0.0" + }, + "engines": { + "node": ">=6.9" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/html-webpack-plugin/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/style-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", + "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^2.7.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/@storybook/manager-webpack4/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/node-logger": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.4.19.tgz", + "integrity": "sha512-hO2Aar3PgPnPtNq2fVgiuGlqo3EEVR6TKVBXMq7foL3tN2k4BQFKLDHbm5qZQQntyYKurKsRUGKPJFPuI1ov/w==", + "dev": true, + "dependencies": { + "@types/npmlog": "^4.1.2", + "chalk": "^4.1.0", + "core-js": "^3.8.2", + "npmlog": "^5.0.1", + "pretty-hrtime": "^1.0.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/node-logger/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@storybook/node-logger/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@storybook/node-logger/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/node-logger/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/preview-web": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/preview-web/-/preview-web-6.4.19.tgz", + "integrity": "sha512-jqltoBv5j7lvnxEfV9w8dLX9ASWGuvgz97yg8Yo5FqkftEwrHJenyvMGcTgDJKJPorF+wiz/9aIqnmd3LCAcZQ==", + "dev": true, + "dependencies": { + "@storybook/addons": "6.4.19", + "@storybook/channel-postmessage": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "@storybook/store": "6.4.19", + "ansi-to-html": "^0.6.11", + "core-js": "^3.8.2", + "global": "^4.4.0", + "lodash": "^4.17.21", + "qs": "^6.10.0", + "regenerator-runtime": "^0.13.7", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "unfetch": "^4.2.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@storybook/react": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-6.4.19.tgz", + "integrity": "sha512-5b3i8jkVrjQGmcxxxXwCduHPIh+cluWkfeweKeQOe+lW4BR8fuUICo3AMLrYPAtB/UcaJyYkIYmTvF2mkfepFA==", + "dev": true, + "dependencies": { + "@babel/preset-flow": "^7.12.1", + "@babel/preset-react": "^7.12.10", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1", + "@storybook/addons": "6.4.19", + "@storybook/core": "6.4.19", + "@storybook/core-common": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "@storybook/node-logger": "6.4.19", + "@storybook/react-docgen-typescript-plugin": "1.0.2-canary.253f8c1.0", + "@storybook/semver": "^7.3.2", + "@storybook/store": "6.4.19", + "@types/webpack-env": "^1.16.0", + "babel-plugin-add-react-displayname": "^0.0.5", + "babel-plugin-named-asset-import": "^0.3.1", + "babel-plugin-react-docgen": "^4.2.1", + "core-js": "^3.8.2", + "global": "^4.4.0", + "lodash": "^4.17.21", + "prop-types": "^15.7.2", + "react-refresh": "^0.11.0", + "read-pkg-up": "^7.0.1", + "regenerator-runtime": "^0.13.7", + "ts-dedent": "^2.0.0", + "webpack": "4" + }, + "bin": { + "build-storybook": "bin/build.js", + "start-storybook": "bin/index.js", + "storybook-server": "bin/index.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@babel/core": "^7.11.5", + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin": { + "version": "1.0.2-canary.253f8c1.0", + "resolved": "https://registry.npmjs.org/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.2-canary.253f8c1.0.tgz", + "integrity": "sha512-mmoRG/rNzAiTbh+vGP8d57dfcR2aP+5/Ll03KKFyfy5FqWFm/Gh7u27ikx1I3LmVMI8n6jh5SdWMkMKon7/tDw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "endent": "^2.0.1", + "find-cache-dir": "^3.3.1", + "flat-cache": "^3.0.4", + "micromatch": "^4.0.2", + "react-docgen-typescript": "^2.0.0", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "typescript": ">= 3.x", + "webpack": ">= 4" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/react-docgen-typescript-plugin/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/@storybook/router": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.4.19.tgz", + "integrity": "sha512-KWWwIzuyeEIWVezkCihwY2A76Il9tUNg0I410g9qT7NrEsKyqXGRYOijWub7c1GGyNjLqz0jtrrehtixMcJkuA==", + "dev": true, + "dependencies": { + "@storybook/client-logger": "6.4.19", + "core-js": "^3.8.2", + "fast-deep-equal": "^3.1.3", + "global": "^4.4.0", + "history": "5.0.0", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "react-router": "^6.0.0", + "react-router-dom": "^6.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@storybook/router/node_modules/react-router": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.2.tgz", + "integrity": "sha512-/MbxyLzd7Q7amp4gDOGaYvXwhEojkJD5BtExkuKmj39VEE0m3l/zipf6h2WIB2jyAO0lI6NGETh4RDcktRm4AQ==", + "dev": true, + "dependencies": { + "history": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@storybook/router/node_modules/react-router-dom": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.2.tgz", + "integrity": "sha512-AtYEsAST7bDD4dLSQHDnk/qxWLJdad5t1HFa1qJyUrCeGgEuCSw0VB/27ARbF9Fi/W5598ujvJOm3ujUCVzuYQ==", + "dev": true, + "dependencies": { + "history": "^5.2.0", + "react-router": "6.2.2" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@storybook/router/node_modules/react-router-dom/node_modules/history": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.6" + } + }, + "node_modules/@storybook/router/node_modules/react-router/node_modules/history": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.6" + } + }, + "node_modules/@storybook/semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@storybook/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg==", + "dev": true, + "dependencies": { + "core-js": "^3.6.5", + "find-up": "^4.1.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@storybook/semver/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/semver/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/semver/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/store": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/store/-/store-6.4.19.tgz", + "integrity": "sha512-N9/ZjemRHGfT3InPIbqQqc6snkcfnf3Qh9oOr0smbfaVGJol//KOX65kzzobtzFcid0WxtTDZ3HmgFVH+GvuhQ==", + "dev": true, + "dependencies": { + "@storybook/addons": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/csf": "0.0.2--canary.87bc651.0", + "core-js": "^3.8.2", + "fast-deep-equal": "^3.1.3", + "global": "^4.4.0", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "regenerator-runtime": "^0.13.7", + "slash": "^3.0.0", + "stable": "^0.1.8", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@storybook/store/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@storybook/theming": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.19.tgz", + "integrity": "sha512-V4pWmTvAxmbHR6B3jA4hPkaxZPyExHvCToy7b76DpUTpuHihijNDMAn85KhOQYIeL9q14zP/aiz899tOHsOidg==", + "dev": true, + "dependencies": { + "@emotion/core": "^10.1.1", + "@emotion/is-prop-valid": "^0.8.6", + "@emotion/styled": "^10.0.27", + "@storybook/client-logger": "6.4.19", + "core-js": "^3.8.2", + "deep-object-diff": "^1.1.0", + "emotion-theming": "^10.0.27", + "global": "^4.4.0", + "memoizerific": "^1.11.3", + "polished": "^4.0.5", + "resolve-from": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@storybook/ui": { + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.19.tgz", + "integrity": "sha512-gFwdn5LA2U6oQ4bfUFLyHZnNasGQ01YVdwjbi+l6yjmnckBNtZfJoVTZ1rzGUbxSE9rK48InJRU+latTsr7xAg==", + "dev": true, + "dependencies": { + "@emotion/core": "^10.1.1", + "@storybook/addons": "6.4.19", + "@storybook/api": "6.4.19", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/components": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/router": "6.4.19", + "@storybook/semver": "^7.3.2", + "@storybook/theming": "6.4.19", + "copy-to-clipboard": "^3.3.1", + "core-js": "^3.8.2", + "core-js-pure": "^3.8.2", + "downshift": "^6.0.15", + "emotion-theming": "^10.0.27", + "fuse.js": "^3.6.1", + "global": "^4.4.0", + "lodash": "^4.17.21", + "markdown-to-jsx": "^7.1.3", + "memoizerific": "^1.11.3", + "polished": "^4.0.5", + "qs": "^6.10.0", + "react-draggable": "^4.4.3", + "react-helmet-async": "^1.0.7", + "react-sizeme": "^3.0.1", + "regenerator-runtime": "^0.13.7", + "resolve-from": "^5.0.0", + "store2": "^2.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0", + "react-dom": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/color-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.0.tgz", + "integrity": "sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==", + "dev": true, + "dependencies": { + "@types/color-name": "*" + } + }, + "node_modules/@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "node_modules/@types/css-modules-loader-core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", + "integrity": "sha512-LMbyf7THPqLCPHIXAj79v9Pa193MeOHgp1fBFRR6s6VvEVHUFIcM5bc/WttslOf+lao4TURNN1X1zfW5wr2CHQ==", + "dev": true, + "dependencies": { + "postcss": "7.x.x" + } + }, + "node_modules/@types/d3": { + "version": "3.5.38", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.38.tgz", + "integrity": "sha1-dvjy6RWa5WKWWy+g5vvuGqZDobw=" + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz", + "integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", + "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==", + "dev": true + }, + "node_modules/@types/is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "node_modules/@types/mdast": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", + "dev": true + }, + "node_modules/@types/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/@types/npmlog": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/npmlog/-/npmlog-4.1.4.tgz", + "integrity": "sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==", + "dev": true + }, + "node_modules/@types/overlayscrollbars": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@types/overlayscrollbars/-/overlayscrollbars-1.12.1.tgz", + "integrity": "sha512-V25YHbSoKQN35UasHf0EKD9U2vcmexRSp78qa8UglxFH8H3D+adEa9zGZwrqpH4TdvqeMrgMqVqsLB4woAryrQ==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/parse5": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz", + "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==", + "dev": true + }, + "node_modules/@types/pretty-hrtime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz", + "integrity": "sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, + "node_modules/@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/react": { + "version": "17.0.43", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", + "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-syntax-highlighter": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz", + "integrity": "sha512-VIOi9i2Oj5XsmWWoB72p3KlZoEbdRAcechJa8Ztebw7bDl2YmR+odxIqhtJGp1q2EozHs02US+gzxJ9nuf56qg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react/node_modules/csstype": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", + "dev": true + }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, + "node_modules/@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "node_modules/@types/tapable": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz", + "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==", + "dev": true + }, + "node_modules/@types/uglify-js": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz", + "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==", + "dev": true, + "dependencies": { + "source-map": "^0.6.1" + } + }, + "node_modules/@types/uglify-js/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", + "dev": true + }, + "node_modules/@types/webpack": { + "version": "4.41.32", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz", + "integrity": "sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tapable": "^1", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "anymatch": "^3.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@types/webpack-env": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.3.tgz", + "integrity": "sha512-9gtOPPkfyNoEqCQgx4qJKkuNm/x0R2hKR7fdl7zvTJyHnIisuE/LfvXOsYWL0o3qq6uiBnKZNNNzi3l0y/X+xw==", + "dev": true + }, + "node_modules/@types/webpack-sources": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.0.tgz", + "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + } + }, + "node_modules/@types/webpack/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "dependencies": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "node_modules/@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "dev": true, + "optional": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/add-dom-event-listener": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz", + "integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==", + "dependencies": { + "object-assign": "4.x" + } + }, + "node_modules/address": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.1.2.tgz", + "integrity": "sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==", + "dev": true, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/airbnb-js-shims": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/airbnb-js-shims/-/airbnb-js-shims-2.2.1.tgz", + "integrity": "sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "array.prototype.flatmap": "^1.2.1", + "es5-shim": "^4.5.13", + "es6-shim": "^0.35.5", + "function.prototype.name": "^1.1.0", + "globalthis": "^1.0.0", + "object.entries": "^1.1.0", + "object.fromentries": "^2.0.0 || ^1.0.0", + "object.getownpropertydescriptors": "^2.0.3", + "object.values": "^1.1.0", + "promise.allsettled": "^1.0.0", + "promise.prototype.finally": "^3.1.0", + "string.prototype.matchall": "^4.0.0 || ^3.0.1", + "string.prototype.padend": "^3.0.0", + "string.prototype.padstart": "^3.0.0", + "symbol.prototype.description": "^1.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true, + "peerDependencies": { + "ajv": ">=5.0.0" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/ansi-to-html": { + "version": "0.6.15", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.6.15.tgz", + "integrity": "sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ==", + "dev": true, + "dependencies": { + "entities": "^2.0.0" + }, + "bin": { + "ansi-to-html": "bin/ansi-to-html" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/app-root-dir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz", + "integrity": "sha1-OBh+wt6nV3//Az/8sSFyaS/24Rg=", + "dev": true + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/are-we-there-yet/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/are-we-there-yet/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", + "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", + "integrity": "sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz", + "integrity": "sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.4.tgz", + "integrity": "sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "dependencies": { + "object-assign": "^4.1.1", + "util": "0.10.3" + } + }, + "node_modules/assert/node_modules/inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "node_modules/assert/node_modules/util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "dependencies": { + "inherits": "2.0.1" + } + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-types": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "node_modules/ast-types/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", + "dev": true + }, + "node_modules/async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-7.2.6.tgz", + "integrity": "sha512-Iq8TRIB+/9eQ8rbGhcP7ct5cYb/3qjNYAR2SnzLCEcwF6rvVOax8+9+fccgXk4bEhQGjOZd5TLhsksmAdsbGqQ==", + "dev": true, + "dependencies": { + "browserslist": "^2.11.3", + "caniuse-lite": "^1.0.30000805", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.17", + "postcss-value-parser": "^3.2.3" + }, + "bin": { + "autoprefixer-info": "bin/autoprefixer-info" + } + }, + "node_modules/autoprefixer/node_modules/browserslist": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", + "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", + "deprecated": "Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools.", + "dev": true, + "dependencies": { + "caniuse-lite": "^1.0.30000792", + "electron-to-chromium": "^1.3.30" + }, + "bin": { + "browserslist": "cli.js" + } + }, + "node_modules/autoprefixer/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/autoprefixer/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1102.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1102.0.tgz", + "integrity": "sha512-MMOncE8IG3Dop3WPza6ryTAEz413ftn/MtDO7ouessb3ljlg5BfqRkTe/rhPH5svqEqJvlh7qHnK0VjgJwmLTQ==", + "dev": true, + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/axe-core": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz", + "integrity": "sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "node_modules/babel-loader": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz", + "integrity": "sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/babel-loader/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/babel-plugin-add-react-displayname": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz", + "integrity": "sha1-M51M3be2X9YtHfnbn+BN4TQSK9U=", + "dev": true + }, + "node_modules/babel-plugin-apply-mdx-type-prop": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz", + "integrity": "sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "7.10.4", + "@mdx-js/util": "1.6.22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@babel/core": "^7.11.6" + } + }, + "node_modules/babel-plugin-apply-mdx-type-prop/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, + "node_modules/babel-plugin-emotion": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz", + "integrity": "sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@emotion/hash": "0.8.0", + "@emotion/memoize": "0.7.4", + "@emotion/serialize": "^0.11.16", + "babel-plugin-macros": "^2.0.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^1.0.5", + "find-root": "^1.1.0", + "source-map": "^0.5.7" + } + }, + "node_modules/babel-plugin-emotion/node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "node_modules/babel-plugin-emotion/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-emotion/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/babel-plugin-extract-import-names": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz", + "integrity": "sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "7.10.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/babel-plugin-extract-import-names/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "dev": true, + "peerDependencies": { + "@babel/core": "^7.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz", + "integrity": "sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.1", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz", + "integrity": "sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-react-docgen": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz", + "integrity": "sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ==", + "dev": true, + "dependencies": { + "ast-types": "^0.14.2", + "lodash": "^4.17.15", + "react-docgen": "^5.0.0" + } + }, + "node_modules/babel-plugin-recharts": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/babel-plugin-recharts/-/babel-plugin-recharts-1.2.1.tgz", + "integrity": "sha512-bIPmjP3TrF2RJk0jDBG+91aTVyLuK48T4Nqekvs/B6MdzPmHXBKB+q+8xXCSRth08Wck+X5ylgSTcr3Ab1egvw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.1.0", + "babylon": "^6.18.0" + } + }, + "node_modules/babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", + "dev": true + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/babel-runtime/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/babel-runtime/node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "node_modules/babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true, + "bin": { + "babylon": "bin/babylon.js" + } + }, + "node_modules/backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "node_modules/batch-processor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", + "integrity": "sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg=", + "dev": true + }, + "node_modules/better-opn": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-2.1.1.tgz", + "integrity": "sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA==", + "dev": true, + "dependencies": { + "open": "^7.0.3" + }, + "engines": { + "node": ">8.0.0" + } + }, + "node_modules/bfj": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/block-stream2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", + "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/block-stream2/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/block-stream2/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/block-stream2/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "node_modules/bonjour/node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "node_modules/boxen": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.2", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", + "dependencies": { + "quote-stream": "^1.0.1", + "resolve": "^1.1.5", + "static-module": "^2.2.0", + "through2": "^2.0.0" + }, + "bin": { + "brfs": "bin/cmd.js" + } + }, + "node_modules/brfs/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/brfs/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/brfs/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "node_modules/browser-or-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-1.3.0.tgz", + "integrity": "sha512-0F2z/VSnLbmEeBcUrSuDH5l0HxTXdQQzLjkmBR4cYfvg1zJrKSlmIZFqyFR8oX0NrwPhy3c3HQ6i3OxMbew4Tg==", + "dev": true + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/browserify-sign/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "dependencies": { + "pako": "~1.0.5" + } + }, + "node_modules/browserslist": { + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c8": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.0.tgz", + "integrity": "sha512-XqPyj1uvlHMr+Y1IeRndC2X5P7iJzJlEJwBpCdBbq2JocXOgJfr+JVfJkyNMGROke5LfKrhSFXGFXnwnRJAUJw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.2", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.0.1", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.0.2", + "rimraf": "^3.0.0", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^8.0.0", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.7" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/c8/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/c8/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/c8/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/calendar": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/calendar/-/calendar-0.1.1.tgz", + "integrity": "sha512-CExn+MxSU1pCsjYiS+Cx8d1MtIZqezxRhpIu8odr1oqvYG/0C1m8lvehHxTl1FslgKbOD7Z5QuEcyk8sGgpZLQ==" + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001322", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz", + "integrity": "sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ccount": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", + "integrity": "sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/charcodes": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/charcodes/-/charcodes-0.2.0.tgz", + "integrity": "sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/check-types": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/circular-dependency-plugin": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", + "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "webpack": ">=4.0.1" + } + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/class-utils/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dependencies": { + "exit": "0.1.2", + "glob": "^7.1.1" + }, + "engines": { + "node": ">=0.2.5" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz", + "integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "1.4.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dev": true, + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/codemirror": { + "version": "5.65.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.2.tgz", + "integrity": "sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA==" + }, + "node_modules/collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz", + "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/colord": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", + "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "node_modules/component-classes": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz", + "integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=", + "dependencies": { + "component-indexof": "0.0.3" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/component-indexof": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz", + "integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ=" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz", + "integrity": "sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dependencies": { + "date-now": "^0.1.4" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "node_modules/constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "node_modules/copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "dependencies": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", + "integrity": "sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ==", + "dev": true, + "dependencies": { + "cacache": "^12.0.3", + "find-cache-dir": "^2.1.0", + "glob-parent": "^3.1.0", + "globby": "^7.1.1", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "minimatch": "^3.0.4", + "normalize-path": "^3.0.0", + "p-limit": "^2.2.1", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "dependencies": { + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "node_modules/copy-webpack-plugin/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/copy-webpack-plugin/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", + "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", + "dev": true, + "dependencies": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/core-js-pure": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", + "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/country-data": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/country-data/-/country-data-0.0.31.tgz", + "integrity": "sha1-gJZrjh0Uf6bWpYnTKTP4eTd0lW0=", + "dev": true, + "dependencies": { + "currency-symbol-map": "~2", + "underscore": ">1.4.4" + } + }, + "node_modules/cp-file": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-7.0.0.tgz", + "integrity": "sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "nested-error-stacks": "^2.0.0", + "p-event": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cp-file/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cpy": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/cpy/-/cpy-8.1.2.tgz", + "integrity": "sha512-dmC4mUesv0OYH2kNFEidtf/skUwv4zePmGeepjyyJ0qTo5+8KhA1o99oIAwVVLzQMAeDJml74d6wPPKb6EZUTg==", + "dev": true, + "dependencies": { + "arrify": "^2.0.1", + "cp-file": "^7.0.0", + "globby": "^9.2.0", + "has-glob": "^1.0.0", + "junk": "^3.1.0", + "nested-error-stacks": "^2.1.0", + "p-all": "^2.1.0", + "p-filter": "^2.1.0", + "p-map": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cpy/node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cpy/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "dependencies": { + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cpy/node_modules/fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "dependencies": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/cpy/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/cpy/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cpy/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/cpy/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/cpy/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cpy/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cpy/node_modules/path-type/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cpy/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-react-class": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz", + "integrity": "sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==", + "dependencies": { + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/css-animation": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/css-animation/-/css-animation-1.6.1.tgz", + "integrity": "sha512-/48+/BaEaHRY6kNQ2OIPzKf9A6g8WjZYjhiNDNuIVbsm5tXCGIAsHDjB4Xu1C4vXJtUWZo26O68OQkDpNBaPog==", + "dependencies": { + "babel-runtime": "6.x", + "component-classes": "^1.2.5" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", + "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/css-loader/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/css-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/css-loader/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/css-loader/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/css-loader/node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-modules-loader-core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz", + "integrity": "sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=", + "dev": true, + "dependencies": { + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.1", + "postcss-modules-extract-imports": "1.1.0", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/chalk/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/postcss": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.1.tgz", + "integrity": "sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/postcss-modules-extract-imports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", + "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", + "dev": true, + "dependencies": { + "postcss": "^6.0.1" + } + }, + "node_modules/css-modules-loader-core/node_modules/postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/css-modules-loader-core/node_modules/postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "dependencies": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "node_modules/css-modules-loader-core/node_modules/postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "dependencies": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "node_modules/css-modules-loader-core/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-modules-loader-core/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "dependencies": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", + "dev": true + }, + "node_modules/css-selector-tokenizer": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", + "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "fastparse": "^1.1.2" + } + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, + "node_modules/css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.5.tgz", + "integrity": "sha512-VZO1e+bRRVixMeia1zKagrv0lLN1B/r/u12STGNNUFxnp97LIFgZHQa0JxqlwEkvzUyA9Oz/WnCTAFkdEbONmg==", + "dev": true, + "dependencies": { + "cssnano-preset-default": "^5.2.5", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.5.tgz", + "integrity": "sha512-WopL7PzN7sos3X8B54/QGl+CZUh1f0qN4ds+y2d5EPwRSSc3jsitVw81O+Uyop0pXyOfPfZxnc+LmA8w/Ki/WQ==", + "dev": true, + "dependencies": { + "css-declaration-sorter": "^6.0.3", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.0", + "postcss-discard-comments": "^5.1.1", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.3", + "postcss-merge-rules": "^5.1.1", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.2", + "postcss-minify-selectors": "^5.2.0", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.0", + "postcss-normalize-repeat-style": "^5.1.0", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.1", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dev": true, + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/csso/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/csstype": { + "version": "2.6.20", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", + "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==", + "dev": true + }, + "node_modules/currency-symbol-map": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/currency-symbol-map/-/currency-symbol-map-2.2.0.tgz", + "integrity": "sha1-KzwYcv8aws5ZXYJz5Y4f/wJyrqI=", + "dev": true + }, + "node_modules/cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/d3": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", + "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=" + }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "node_modules/d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "node_modules/d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "node_modules/d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "node_modules/d3-geo-projection": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz", + "integrity": "sha1-SZTs0QM92xUztsTFUoocgdzClCc=", + "dependencies": { + "brfs": "^1.3.0" + } + }, + "node_modules/d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "dependencies": { + "d3-color": "1" + } + }, + "node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-queue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-2.0.3.tgz", + "integrity": "sha1-B/vaOsrlNYqcUpmq+ICt8JU+0sI=" + }, + "node_modules/d3-scale": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", + "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "dependencies": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + }, + "node_modules/d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "dependencies": { + "d3-time": "1" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "node_modules/datamaps": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/datamaps/-/datamaps-0.5.9.tgz", + "integrity": "sha512-GUXpO713URNzaExVUgBtqA5fr2UuxUG/fVitI04zEFHVL2FHSjd672alHq8E16oQqRNzF0m1bmx8WlTnDrGSqQ==", + "dependencies": { + "@types/d3": "3.5.38", + "d3": "^3.5.6", + "topojson": "^1.6.19" + } + }, + "node_modules/date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "node_modules/deasync": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz", + "integrity": "sha512-i98vg42xNfRZCymummMAN0rIcQ1gZFinSe3btvPIvy6JFTaeHcumeKybRo2HTv86nasfmT0nEgAn2ggLZhOCVA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^1.7.1" + }, + "engines": { + "node": ">=0.11.0" + } + }, + "node_modules/deasync-promise": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deasync-promise/-/deasync-promise-1.0.1.tgz", + "integrity": "sha1-KyfeR4Fnr07zS6mYecUuwM7dYcI=", + "dev": true, + "dependencies": { + "deasync": "^0.1.7" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" + }, + "node_modules/deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dependencies": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, + "node_modules/deep-object-diff": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.7.tgz", + "integrity": "sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "dependencies": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "node_modules/del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/del/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/deploy-aws-s3-cloudfront": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/deploy-aws-s3-cloudfront/-/deploy-aws-s3-cloudfront-3.6.0.tgz", + "integrity": "sha512-nR1xDXRbV1uDERaP+wMPYrXlKDoWGO+x9DARzSQEluU7qa4zz7EjhfUIAZa4nWBUPZvl1wQtbVmvtCByY+p+Zw==", + "dev": true, + "dependencies": { + "aws-sdk": "2", + "enquirer": "2", + "fast-glob": "3", + "md5-file": "5", + "micromatch": "4", + "mime-types": "2", + "winston": "3", + "yargs": "17" + }, + "bin": { + "deploy-aws-s3-cloudfront": "bin/deploy-aws-s3-cloudfront" + } + }, + "node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "node_modules/detab": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detab/-/detab-2.0.4.tgz", + "integrity": "sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g==", + "dev": true, + "dependencies": { + "repeat-string": "^1.5.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/detect-port": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.3.0.tgz", + "integrity": "sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ==", + "dev": true, + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dependencies": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dnd-core": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-15.1.1.tgz", + "integrity": "sha512-Mtj/Sltcx7stVXzeDg4g7roTe/AmzRuIf/FYOxX6F8gULbY54w066BlErBOzQfn9RIJ3gAYLGX7wvVvoBSq7ig==", + "dependencies": { + "@react-dnd/asap": "4.0.0", + "@react-dnd/invariant": "3.0.0", + "redux": "^4.1.1" + } + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "node_modules/dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "dependencies": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "dependencies": { + "buffer-indexof": "^1.0.0" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-align": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.2.tgz", + "integrity": "sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg==" + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "dependencies": { + "utila": "~0.4" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dom-helpers/node_modules/csstype": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true + }, + "node_modules/domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true, + "engines": { + "node": ">=0.4", + "npm": ">=1.2" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/dotenv": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", + "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "node_modules/downshift": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-6.1.7.tgz", + "integrity": "sha512-cVprZg/9Lvj/uhYRxELzlu1aezRcgPWBjTvspiGTVEU64gF5pRdSRKFVLcxqsZC637cLAGMbL40JavEfWnqgNg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.14.8", + "compute-scroll-into-view": "^1.0.17", + "prop-types": "^15.7.2", + "react-is": "^17.0.2", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.12.0" + } + }, + "node_modules/downshift/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "node_modules/ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.97", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.97.tgz", + "integrity": "sha512-vqSu7Qn6o5E1uAJQxmq2U69aBhBTxUAXMuT5Sm3jj8kEJciuUcKciktLuTPFSRlwSdNyeu9qah8Nzy9JyxefCw==" + }, + "node_modules/element-resize-detector": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.4.tgz", + "integrity": "sha512-Fl5Ftk6WwXE0wqCgNoseKWndjzZlDCwuPTcoVZfCP9R3EHQF8qUtr3YUPNETegRBOKqQKPW3n4kiIWngGi8tKg==", + "dev": true, + "dependencies": { + "batch-processor": "1.0.0" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/emotion-theming": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emotion-theming/-/emotion-theming-10.3.0.tgz", + "integrity": "sha512-mXiD2Oj7N9b6+h/dC6oLf9hwxbtKHQjoIqtodEyL8CpkN4F3V4IK/BT4D0C7zSs4BBFOu4UlPJbvvBLa88SGEA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.5.5", + "@emotion/weak-memoize": "0.2.5", + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "@emotion/core": "^10.0.27", + "react": ">=16.3.0" + } + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/endent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/endent/-/endent-2.1.0.tgz", + "integrity": "sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==", + "dev": true, + "dependencies": { + "dedent": "^0.7.0", + "fast-json-parse": "^1.0.3", + "objectorarray": "^1.0.5" + } + }, + "node_modules/engine.io-client": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.1.1.tgz", + "integrity": "sha512-V05mmDo4gjimYW+FGujoGmmmxRaDsrVr7AXA3ZIfa04MWM1jOfZfUwou0oNqhNwy/votUDvGDt4JA4QF4e0b4g==", + "dependencies": { + "@socket.io/component-emitter": "~3.0.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.0", + "has-cors": "1.1.0", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~8.2.3", + "xmlhttprequest-ssl": "~2.0.0", + "yeast": "0.1.2" + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.3.tgz", + "integrity": "sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg==", + "dependencies": { + "@socket.io/base64-arraybuffer": "~1.0.2" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.7.tgz", + "integrity": "sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA==", + "dev": true, + "dependencies": { + "stackframe": "^1.1.1" + } + }, + "node_modules/es-abstract": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", + "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "node_modules/es-get-iterator": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", + "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.0", + "has-symbols": "^1.0.1", + "is-arguments": "^1.1.0", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-get-iterator/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.59", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.59.tgz", + "integrity": "sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es5-shim": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.5.tgz", + "integrity": "sha512-vfQ4UAai8szn0sAubCy97xnZ4sJVDD1gt/Grn736hg8D7540wemIb1YPrYZSTqlM2H69EQX1or4HU/tSwRTI3w==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-shim": { + "version": "0.35.6", + "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.6.tgz", + "integrity": "sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==", + "dev": true + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", + "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "dependencies": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-config-airbnb": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz", + "integrity": "sha512-zLyOhVWhzB/jwbz7IPSbkUuj7X2ox4PHXTcZkEmDqTvd0baJmJyuxlFPDlZOE/Y5bC+HQRaEkT3FoHo9wIdRiw==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^12.1.0" + }, + "engines": { + "node": ">= 4" + }, + "peerDependencies": { + "eslint": "^4.9.0", + "eslint-plugin-import": "^2.7.0", + "eslint-plugin-jsx-a11y": "^6.0.2", + "eslint-plugin-react": "^7.4.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz", + "integrity": "sha512-/vjm0Px5ZCpmJqnjIzcFb9TKZrKWz0gnuG/7Gfkt0Db1ELJR51xkZth+t14rYdqWgX836XbuxtArbIHlVhbLBA==", + "dev": true, + "dependencies": { + "eslint-restricted-globals": "^0.1.1" + }, + "engines": { + "node": ">= 4" + }, + "peerDependencies": { + "eslint": "^4.9.0", + "eslint-plugin-import": "^2.7.0" + } + }, + "node_modules/eslint-import-resolver-babel-module": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-babel-module/-/eslint-import-resolver-babel-module-4.0.0.tgz", + "integrity": "sha512-aPj0+pG0H3HCaMD9eRDYEzPdMyKrLE2oNhAzTXd2w86ZBe3s7drSrrPwVTfzO1CBp13FGk8S84oRmZHZvSo0mA==", + "dev": true, + "dependencies": { + "pkg-up": "^2.0.0", + "resolve": "^1.4.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "babel-core": "^6.0.0", + "babel-plugin-module-resolver": "^3.0.0-beta" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-module-utils/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.25.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz", + "integrity": "sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.2", + "has": "^1.0.3", + "is-core-module": "^2.8.0", + "is-glob": "^4.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.5", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.12.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz", + "integrity": "sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.16.3", + "aria-query": "^4.2.2", + "array-includes": "^3.1.4", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.3.5", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.7", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.2.1", + "language-tags": "^1.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", + "integrity": "sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.4", + "array.prototype.flatmap": "^1.2.5", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.5", + "object.fromentries": "^2.0.5", + "object.hasown": "^1.1.0", + "object.values": "^1.1.5", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.3", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-restricted-globals": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", + "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", + "dev": true + }, + "node_modules/eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-to-babel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/estree-to-babel/-/estree-to-babel-3.2.1.tgz", + "integrity": "sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.6", + "@babel/types": "^7.2.0", + "c8": "^7.6.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/eventsource": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", + "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", + "dev": true, + "dependencies": { + "original": "^1.0.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/express/node_modules/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "dev": true, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "dependencies": { + "type": "^2.5.0" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", + "dev": true + }, + "node_modules/falafel": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.4.tgz", + "integrity": "sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ==", + "dependencies": { + "acorn": "^7.1.1", + "foreach": "^2.0.5", + "isarray": "^2.0.1", + "object-keys": "^1.0.6" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/falafel/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-parse": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fast-json-parse/-/fast-json-parse-1.0.3.tgz", + "integrity": "sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "node_modules/fast-xml-parser": { + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz", + "integrity": "sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==", + "dev": true, + "dependencies": { + "strnum": "^1.0.4" + }, + "bin": { + "xml2js": "cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, + "node_modules/fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "dev": true, + "dependencies": { + "format": "^0.2.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "dependencies": { + "fbjs": "^3.0.0" + } + }, + "node_modules/fbjs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", + "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.30" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, + "node_modules/fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==", + "dev": true + }, + "node_modules/figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "dev": true + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-system-cache": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/file-system-cache/-/file-system-cache-1.0.5.tgz", + "integrity": "sha1-hCWbNqK7uNPW6xAh0xMv/mTP/08=", + "dev": true, + "dependencies": { + "bluebird": "^3.3.5", + "fs-extra": "^0.30.0", + "ramda": "^0.21.0" + } + }, + "node_modules/file-system-cache/node_modules/fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "node_modules/file-system-cache/node_modules/jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true + }, + "node_modules/filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/findup-sync/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/findup-sync/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/findup-sync/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "node_modules/flow-bin": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.115.0.tgz", + "integrity": "sha512-xW+U2SrBaAr0EeLvKmXAmsdnrH6x0Io17P6yRJTNgrrV42G8KXhBAD00s6oGbTTqRyHD0nP47kyuU34zljZpaQ==", + "dev": true, + "bin": { + "flow": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/flush-write-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/flush-write-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/flux": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", + "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", + "dependencies": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + }, + "peerDependencies": { + "react": "^15.0.2 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz", + "integrity": "sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/from2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/from2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "node_modules/fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.2.tgz", + "integrity": "sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuse.js": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz", + "integrity": "sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-promise": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-3.4.0.tgz", + "integrity": "sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw==", + "dev": true, + "dependencies": { + "@types/glob": "*" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "glob": "*" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dev": true, + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", + "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + }, + "node_modules/gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, + "node_modules/gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-glob/-/has-glob-1.0.0.tgz", + "integrity": "sha1-mqqe7b/7G6OZCnsAEPtnjuAIEgc=", + "dev": true, + "dependencies": { + "is-glob": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-glob/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/hash-base/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hast-to-hyperscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", + "integrity": "sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.3", + "comma-separated-tokens": "^1.0.0", + "property-information": "^5.3.0", + "space-separated-tokens": "^1.0.0", + "style-to-object": "^0.3.0", + "unist-util-is": "^4.0.0", + "web-namespaces": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz", + "integrity": "sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA==", + "dev": true, + "dependencies": { + "@types/parse5": "^5.0.0", + "hastscript": "^6.0.0", + "property-information": "^5.0.0", + "vfile": "^4.0.0", + "vfile-location": "^3.2.0", + "web-namespaces": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-6.0.1.tgz", + "integrity": "sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-from-parse5": "^6.0.0", + "hast-util-to-parse5": "^6.0.0", + "html-void-elements": "^1.0.0", + "parse5": "^6.0.0", + "unist-util-position": "^3.0.0", + "vfile": "^4.0.0", + "web-namespaces": "^1.0.0", + "xtend": "^4.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz", + "integrity": "sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ==", + "dev": true, + "dependencies": { + "hast-to-hyperscript": "^9.0.0", + "property-information": "^5.0.0", + "web-namespaces": "^1.0.0", + "xtend": "^4.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "dev": true, + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, + "node_modules/history": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.0.0.tgz", + "integrity": "sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.7.6" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "dependencies": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/html-minifier-terser": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", + "integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.1", + "clean-css": "^4.2.3", + "commander": "^4.1.1", + "he": "^1.2.0", + "param-case": "^3.0.3", + "relateurl": "^0.2.7", + "terser": "^4.6.3" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/html-minifier-terser/node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/html-minifier-terser/node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/html-minifier-terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/html-minifier-terser/node_modules/terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/html-minifier-terser/node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/html-minifier-terser/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/html-minifier/node_modules/commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "node_modules/html-minifier/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/html-minifier/node_modules/uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "dependencies": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/html-minifier/node_modules/uglify-js/node_modules/commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "node_modules/html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/html-void-elements": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-1.0.5.tgz", + "integrity": "sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "deprecated": "3.x is no longer supported", + "dev": true, + "dependencies": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "engines": { + "node": ">=6.9" + }, + "peerDependencies": { + "webpack": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/html-webpack-plugin/node_modules/big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/html-webpack-plugin/node_modules/emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/html-webpack-plugin/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/html-webpack-plugin/node_modules/loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "dependencies": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "node_modules/htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dependencies": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "dependencies": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/http-proxy-middleware/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy-middleware/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/http-proxy/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "node_modules/icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "node_modules/iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==" + }, + "node_modules/import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "dependencies": { + "import-from": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "dependencies": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==", + "dev": true + }, + "node_modules/internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "dependencies": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/internal-ip/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "node_modules/ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", + "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "dev": true + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "dependencies": { + "is-path-inside": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "dependencies": { + "path-is-inside": "^1.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-there": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/is-there/-/is-there-4.5.1.tgz", + "integrity": "sha512-vIZ7HTXAoRoIwYSsTnxb0sg9L6rth+JOulNcavsbskQkCIWoSM2cjFOWZs4wGziGZER+Xgs/HXiCQZgiL8ppxQ==", + "dev": true + }, + "node_modules/is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", + "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-word-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", + "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterate-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", + "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "dependencies": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbi": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.2.0.tgz", + "integrity": "sha512-JzhwPkH7Ra8O1b5uHmWxl6N8ZumqGvMXgpKcsq0/iWlnB+KkzbekCIcLl3hu3sFGTLNXAuXIqY4STtE0ZfT1zA==" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jshint": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.4.tgz", + "integrity": "sha512-HO3bosL84b2qWqI0q+kpT/OpRJwo0R4ivgmxaO848+bo10rc50SkPnrtwSFXttW0ym4np8jbJvLwk5NziB7jIw==", + "dependencies": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.21", + "minimatch": "~3.0.2", + "strip-json-comments": "1.0.x" + }, + "bin": { + "jshint": "bin/jshint" + } + }, + "node_modules/jshint/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-stream/-/json-stream-1.0.0.tgz", + "integrity": "sha1-GjhU4o0rvuqzHMfd9oPS3cVlJwg=", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", + "integrity": "sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.3", + "object.assign": "^4.1.2" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/junk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz", + "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/keyboard-key": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keyboard-key/-/keyboard-key-1.1.0.tgz", + "integrity": "sha512-qkBzPTi3rlAKvX7k0/ub44sqOfXeLc/jcnGGmj5c7BJpU8eDrEVPyhCvNYAaoubbsLm9uGWwQJO1ytQK1a9/dQ==" + }, + "node_modules/killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", + "dev": true, + "dependencies": { + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/lazy-universal-dotenv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz", + "integrity": "sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.5.0", + "app-root-dir": "^1.0.2", + "core-js": "^3.0.4", + "dotenv": "^8.0.0", + "dotenv-expand": "^5.1.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=6.0.0", + "yarn": ">=1.0.0" + } + }, + "node_modules/lazy-universal-dotenv/node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", + "dev": true + }, + "node_modules/lodash.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "node_modules/logform": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz", + "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "node_modules/loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "dev": true, + "dependencies": { + "fault": "^1.0.0", + "highlight.js": "~10.7.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/luxon": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==", + "engines": { + "node": "*" + } + }, + "node_modules/magic-string": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "dependencies": { + "vlq": "^0.2.2" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-or-similar": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", + "integrity": "sha1-beJlMXSt+12e3DPGnT6Sobdvrwg=", + "dev": true + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-escapes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", + "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/markdown-to-jsx": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz", + "integrity": "sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==", + "dev": true, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, + "node_modules/math-expression-evaluator": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.3.14.tgz", + "integrity": "sha512-M6AMrvq9bO8uL42KvQHPA2/SbAobA0R7gviUmPrcTcGfdwpaLitz4q2Euzx2lP9Oy88vxK3HOrsISgSwKsYS4A==" + }, + "node_modules/md5-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz", + "integrity": "sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==", + "dev": true, + "bin": { + "md5-file": "cli.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdast-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ==", + "dev": true, + "dependencies": { + "unist-util-remove": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "dev": true, + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz", + "integrity": "sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "mdast-util-definitions": "^4.0.0", + "mdurl": "^1.0.0", + "unist-builder": "^2.0.0", + "unist-util-generated": "^1.0.0", + "unist-util-position": "^3.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", + "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", + "dev": true, + "dependencies": { + "fs-monkey": "1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoizerific": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", + "integrity": "sha1-fIekZGREwy11Q4VwkF8tvRsagFo=", + "dev": true, + "dependencies": { + "map-or-similar": "^1.5.0" + } + }, + "node_modules/memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "engines": { + "node": ">=4.3.0 <5.0.0 || >=5.10" + } + }, + "node_modules/memory-fs/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/memory-fs/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "node_modules/merge-source-map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", + "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=", + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/merge-source-map/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/microevent.ts": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz", + "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "dev": true, + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz", + "integrity": "sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ==", + "dev": true, + "dependencies": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.4.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/minio": { + "version": "7.0.26", + "resolved": "https://registry.npmjs.org/minio/-/minio-7.0.26.tgz", + "integrity": "sha512-knutnEZZMIUB/Xln6psVDrqObFKXDcF9m4IfFIX+zgDHYg3AlcF88DY1wdgg7bUkf+uU8iHkzP2q5CXAhia73w==", + "dev": true, + "dependencies": { + "async": "^3.1.0", + "block-stream2": "^2.0.0", + "browser-or-node": "^1.3.0", + "crypto-browserify": "^3.12.0", + "es6-error": "^4.1.1", + "fast-xml-parser": "^3.17.5", + "ipaddr.js": "^2.0.1", + "json-stream": "^1.0.0", + "lodash": "^4.17.21", + "mime-types": "^2.1.14", + "mkdirp": "^0.5.1", + "querystring": "0.2.0", + "through2": "^3.0.1", + "web-encoding": "^1.1.5", + "xml": "^1.0.0", + "xml2js": "^0.4.15" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/minipass": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", + "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "dependencies": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mississippi/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/mississippi/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/mississippi/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mobx": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.5.0.tgz", + "integrity": "sha512-pHZ/cySF00FVENDWIDzJyoObFahK6Eg4d0papqm6d7yMkxWTZ/S/csqJX1A3PsYy4t5k3z2QnlwuCfMW5lSEwA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + } + }, + "node_modules/mobx-react-lite": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.3.0.tgz", + "integrity": "sha512-U/kMSFtV/bNVgY01FuiGWpRkaQVHozBq5CEBZltFvPt4FcV111hEWkgwqVg9GPPZSEuEdV438PEz8mk8mKpYlA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.1.0", + "react": "^16.8.0 || ^17" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-locales-webpack-plugin": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/moment-locales-webpack-plugin/-/moment-locales-webpack-plugin-1.2.0.tgz", + "integrity": "sha512-QAi5v0OlPUP7GXviKMtxnpBAo8WmTHrUNN7iciAhNOEAd9evCOvuN0g1N7ThIg3q11GLCkjY1zQ2saRcf/43nQ==", + "dev": true, + "dependencies": { + "lodash.difference": "^4.5.0" + }, + "peerDependencies": { + "moment": "^2.8.0", + "webpack": "^1 || ^2 || ^3 || ^4 || ^5" + } + }, + "node_modules/moment-range": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/moment-range/-/moment-range-4.0.2.tgz", + "integrity": "sha512-n8sceWwSTjmz++nFHzeNEUsYtDqjgXgcOBzsHi+BoXQU2FW+eU92LUaK8gqOiSu5PG57Q9sYj1Fz4LRDj4FtKA==", + "dependencies": { + "es6-symbol": "^3.1.0" + }, + "engines": { + "node": "*" + }, + "peerDependencies": { + "moment": ">= 2" + } + }, + "node_modules/move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "dependencies": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "dependencies": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "node_modules/nan": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", + "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "dev": true, + "optional": true + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nested-error-stacks": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", + "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true + }, + "node_modules/node-dir": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", + "integrity": "sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU=", + "dev": true, + "dependencies": { + "minimatch": "^3.0.2" + }, + "engines": { + "node": ">= 0.10.5" + } + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "dependencies": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/node-libs-browser/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/node-libs-browser/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "node_modules/node-libs-browser/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/node-libs-browser/node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/node-libs-browser/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/node-libs-browser/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/node-libs-browser/node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/node-libs-browser/node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/node-libs-browser/node_modules/util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "dependencies": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/object-copy/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", + "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", + "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", + "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", + "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/objectorarray": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.5.tgz", + "integrity": "sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==", + "dev": true + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opn/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/optimist": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", + "dependencies": { + "wordwrap": "~0.0.2" + } + }, + "node_modules/optimist/node_modules/wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "dependencies": { + "url-parse": "^1.4.3" + } + }, + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "node_modules/overlayscrollbars": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.1.tgz", + "integrity": "sha512-gIQfzgGgu1wy80EB4/6DaJGHMEGmizq27xHIESrzXq0Y/J0Ay1P3DWk6tuVmEPIZH15zaBlxeEJOqdJKmowHCQ==", + "dev": true + }, + "node_modules/p-all": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-all/-/p-all-2.1.0.tgz", + "integrity": "sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA==", + "dev": true, + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-all/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", + "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "dev": true, + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-filter/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "dependencies": { + "retry": "^0.12.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "dependencies": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "node_modules/parallel-transform/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/parallel-transform/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "node_modules/parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/peerjs": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/peerjs/-/peerjs-1.3.2.tgz", + "integrity": "sha512-+PHfmsC7QGUU8Ye3OLi6tKQZGPCNy7QatUVNw4JtE8alkguF3+DdO5W0bzepqP2OtE9FqH/ltXt37qyvHw2CqA==", + "dependencies": { + "@types/node": "^10.14.33", + "eventemitter3": "^3.1.2", + "peerjs-js-binarypack": "1.0.1", + "webrtc-adapter": "^7.7.1" + } + }, + "node_modules/peerjs-js-binarypack": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/peerjs-js-binarypack/-/peerjs-js-binarypack-1.0.1.tgz", + "integrity": "sha512-N6aeia3NhdpV7kiGxJV5xQiZZCVEEVjRz2T2C6UZQiBkHWHzUv/oWA4myQLcwBwO8LUoR1KWW5oStvwVesmfCg==" + }, + "node_modules/peerjs/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pnp-webpack-plugin": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", + "dev": true, + "dependencies": { + "ts-pnp": "^1.1.6" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/polished": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.1.4.tgz", + "integrity": "sha512-Nq5Mbza+Auo7N3sQb1QMFaQiDO+4UexWuSGR7Cjb4Sw11SZIJcrrFtiZ+L0jT9MBsUsxDboHVASbCLbE1rnECg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.16.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/portfinder/node_modules/async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/portfinder/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-calc/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-colormin/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-convert-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz", + "integrity": "sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz", + "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-flexbugs-fixes": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz", + "integrity": "sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.26" + } + }, + "node_modules/postcss-functions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", + "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=", + "dependencies": { + "glob": "^7.1.2", + "object-assign": "^4.1.1", + "postcss": "^6.0.9", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/postcss-functions/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-functions/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-import": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", + "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", + "dev": true, + "dependencies": { + "postcss": "^7.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-inline-svg": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/postcss-inline-svg/-/postcss-inline-svg-3.1.1.tgz", + "integrity": "sha512-G2BkarW6gGpGFGAiKzW7aiulUS0/6QuCgq1riZEiX4oMaUTpU1pdW7BU6UFRDrdKkwS0r4icK2pU0bg6sCSOjw==", + "dev": true, + "dependencies": { + "css-select": "^1.2.0", + "dom-serializer": "^0.1.0", + "htmlparser2": "^3.9.0", + "postcss": "^6.0.1", + "postcss-value-parser": "^3.2.3" + } + }, + "node_modules/postcss-inline-svg/node_modules/dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "node_modules/postcss-inline-svg/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "node_modules/postcss-inline-svg/node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/postcss-inline-svg/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-inline-svg/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-inline-svg/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/postcss-inline-svg/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-inline-svg/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/postcss-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-2.0.3.tgz", + "integrity": "sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==", + "dependencies": { + "camelcase-css": "^2.0.1", + "postcss": "^7.0.18" + } + }, + "node_modules/postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "dev": true, + "dependencies": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-load-config/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-load-config/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-load-config/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-load-config/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "dependencies": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/postcss-loader/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.3.tgz", + "integrity": "sha512-lX8GPGvZ0iGP/IboM7HXH5JwkXvXod1Rr8H8ixwiA372hArk0zP4ZcCy4z4Prg/bfNlbbTf0KCOjCF9kKnpP/w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-longhand/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz", + "integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dev": true, + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-minify-params": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz", + "integrity": "sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz", + "integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-mixins": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-6.2.3.tgz", + "integrity": "sha512-gfH5d09YilzDn/CLGFA9Lwv7GTezuyHgnAyXC8AfvhUMpl67ZTewhcpNuOgawClCOD+76XePE2IHO1xMgsOlvA==", + "dev": true, + "dependencies": { + "globby": "^8.0.1", + "postcss": "^7.0.21", + "postcss-js": "^2.0.3", + "postcss-simple-vars": "^5.0.2", + "sugarss": "^2.0.0" + } + }, + "node_modules/postcss-mixins/node_modules/@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-mixins/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-mixins/node_modules/fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "dependencies": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-mixins/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/postcss-mixins/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/globby": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "dir-glob": "2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-mixins/node_modules/ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "node_modules/postcss-mixins/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/postcss-mixins/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-mixins/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-mixins/node_modules/postcss-simple-vars": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-5.0.2.tgz", + "integrity": "sha512-xWIufxBoINJv6JiLb7jl5oElgp+6puJwvT5zZHliUSydoLz4DADRB3NDDsYgfKVwojn4TDLiseoC65MuS8oGGg==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14" + } + }, + "node_modules/postcss-mixins/node_modules/slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-mixins/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dev": true, + "dependencies": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "dependencies": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "node_modules/postcss-nested": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-4.2.3.tgz", + "integrity": "sha512-rOv0W1HquRCamWy2kFl3QazJMMe1ku6rCFoAAH+9AcxdbpDeBr6k968MLWuLjvjMcGEip01ak09hKOEgpK9hvw==", + "dependencies": { + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2" + } + }, + "node_modules/postcss-nesting": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-4.2.1.tgz", + "integrity": "sha512-IkyWXICwagCnlaviRexi7qOdwPw3+xVVjgFfGsxmztvRVaNxAlrypOIKqDE5mxY+BVxnId1rnUKBRQoNE2VDaA==", + "dev": true, + "dependencies": { + "postcss": "^6.0.11" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-nesting/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz", + "integrity": "sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz", + "integrity": "sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dev": true, + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss-normalize-url/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz", + "integrity": "sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==", + "dev": true, + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-ordered-values/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz", + "integrity": "sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-simple-vars": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-4.1.0.tgz", + "integrity": "sha512-J/TRomA8EqXhS4VjQJsPCYTFIa9FYN/dkJK/8oZ0BYeVIPx91goqM8T+ljsP57+4bwSEywFOuB7EZ8n1gjjxZw==", + "dev": true, + "dependencies": { + "postcss": "^6.0.9" + } + }, + "node_modules/postcss-simple-vars/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-simple-vars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/postcss-svgo/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/postcss-svgo/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/postcss-svgo/node_modules/css-what": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.0.1.tgz", + "integrity": "sha512-z93ZGFLNc6yaoXAmVhqoSIb+BduplteCt1fepvwhBUQK6MNE4g6fgjpuZKJKp0esUe+vXWlIkwZZjNWoOKw0ZA==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/postcss-svgo/node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/postcss-svgo/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/postcss-svgo/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/postcss-svgo/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/postcss-svgo/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/postcss-svgo/node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/postcss-svgo/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-svgo/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/postcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prettier": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz", + "integrity": "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "dev": true, + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/prismjs": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "node_modules/promise.allsettled": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.5.tgz", + "integrity": "sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ==", + "dev": true, + "dependencies": { + "array.prototype.map": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "iterate-value": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/promise.prototype.finally": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/promise.prototype.finally/-/promise.prototype.finally-3.1.3.tgz", + "integrity": "sha512-EXRF3fC9/0gz4qkt/f5EP5iW4kj9oFpBICNpCNOb/52+8nlHIX07FPLbi/q4qYBQ1xZqivMzTpNQSnArVASolQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/property-information": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", + "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "dev": true, + "dependencies": { + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=" + }, + "node_modules/purgecss": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-2.3.0.tgz", + "integrity": "sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ==", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.0.0", + "postcss": "7.0.32", + "postcss-selector-parser": "^6.0.2" + }, + "bin": { + "purgecss": "bin/purgecss" + } + }, + "node_modules/purgecss/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/purgecss/node_modules/postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "dependencies": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + }, + "node_modules/purgecss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/purgecss/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "dependencies": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quote-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", + "integrity": "sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=", + "dependencies": { + "buffer-equal": "0.0.1", + "minimist": "^1.1.3", + "through2": "^2.0.0" + }, + "bin": { + "quote-stream": "bin/cmd.js" + } + }, + "node_modules/quote-stream/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/quote-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/quote-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/ramda": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz", + "integrity": "sha1-oAGr7bP/YQd9T/HVd9RN536NCjU=", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/rc-align": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-2.4.5.tgz", + "integrity": "sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw==", + "dependencies": { + "babel-runtime": "^6.26.0", + "dom-align": "^1.7.0", + "prop-types": "^15.5.8", + "rc-util": "^4.0.4" + } + }, + "node_modules/rc-animate": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.11.1.tgz", + "integrity": "sha512-1NyuCGFJG/0Y+9RKh5y/i/AalUCA51opyyS/jO2seELpgymZm2u9QV3xwODwEuzkmeQ1BDPxMLmYLcTJedPlkQ==", + "dependencies": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "css-animation": "^1.3.2", + "prop-types": "15.x", + "raf": "^3.4.0", + "rc-util": "^4.15.3", + "react-lifecycles-compat": "^3.0.4" + } + }, + "node_modules/rc-time-picker": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/rc-time-picker/-/rc-time-picker-3.7.3.tgz", + "integrity": "sha512-Lv1Mvzp9fRXhXEnRLO4nW6GLNxUkfAZ3RsiIBsWjGjXXvMNjdr4BX/ayElHAFK0DoJqOhm7c5tjmIYpEOwcUXg==", + "dependencies": { + "classnames": "2.x", + "moment": "2.x", + "prop-types": "^15.5.8", + "raf": "^3.4.1", + "rc-trigger": "^2.2.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "node_modules/rc-trigger": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-2.6.5.tgz", + "integrity": "sha512-m6Cts9hLeZWsTvWnuMm7oElhf+03GOjOLfTuU0QmdB9ZrW7jR2IpI5rpNM7i9MvAAlMAmTx5Zr7g3uu/aMvZAw==", + "dependencies": { + "babel-runtime": "6.x", + "classnames": "^2.2.6", + "prop-types": "15.x", + "rc-align": "^2.4.0", + "rc-animate": "2.x", + "rc-util": "^4.4.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "node_modules/rc-util": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.21.1.tgz", + "integrity": "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==", + "dependencies": { + "add-dom-event-listener": "^1.1.0", + "prop-types": "^15.5.10", + "react-is": "^16.12.0", + "react-lifecycles-compat": "^3.0.4", + "shallowequal": "^1.1.0" + } + }, + "node_modules/rc-util/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-addons-pure-render-mixin": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/react-addons-pure-render-mixin/-/react-addons-pure-render-mixin-15.6.3.tgz", + "integrity": "sha512-e7F2OsLiyYGr9SHWHGlI/FfHRh+kbYx0hNfdN5zivHIf4vzeno7gsRJKXg71E35CpUCnre+JfM6UgWWgsvJBzA==", + "dependencies": { + "object-assign": "^4.1.0" + } + }, + "node_modules/react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, + "node_modules/react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=", + "dependencies": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, + "node_modules/react-circular-progressbar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-circular-progressbar/-/react-circular-progressbar-2.0.4.tgz", + "integrity": "sha512-OfX0ThSxRYEVGaQSt0DlXfyl5w4DbXHsXetyeivmoQrh9xA9bzVPHNf8aAhOIiwiaxX2WYWpLDB3gcpsDJ9oww==", + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/react-codemirror2": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/react-codemirror2/-/react-codemirror2-5.1.0.tgz", + "integrity": "sha512-Cksbgbviuf2mJfMyrKmcu7ycK6zX/ukuQO8dvRZdFWqATf5joalhjFc6etnBdGCcPA2LbhIwz+OPnQxLN/j1Fw==", + "peerDependencies": { + "codemirror": "5.x", + "react": ">=15.5 <=16.x" + } + }, + "node_modules/react-colorful": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.5.1.tgz", + "integrity": "sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg==", + "dev": true, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-confirm": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/react-confirm/-/react-confirm-0.1.24.tgz", + "integrity": "sha512-96qA+mbZyBRmh/3Y5aDgrYLwLndbaRjkP3GlXQtPEQbIH0P66xGcHJ7ui6y/MN85AZWq/V3drA1fJOiEcVkAVA==", + "peerDependencies": { + "react": "^0.14.7 || 15.x || 16.x || 17.x", + "react-dom": "^0.14.7 || 15.x || 16.x || 17.x" + } + }, + "node_modules/react-datepicker": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-2.16.0.tgz", + "integrity": "sha512-TvcmSY27rn0JKvuJuIXNNS+niGQNdgtuG/CsBttVYhPOA9KmSw7c2PvQBPVEvzkyV+QPNJ8jN/KVJNj9uvopqA==", + "dependencies": { + "classnames": "^2.2.6", + "date-fns": "^2.0.1", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.9.0", + "react-popper": "^1.3.4" + }, + "peerDependencies": { + "react": "^16.9.0", + "react-dom": "^16.9.0" + } + }, + "node_modules/react-daterange-picker": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-daterange-picker/-/react-daterange-picker-2.0.1.tgz", + "integrity": "sha512-xPBtMONOfljnausiL6ftlfg6EHHvjdiDcH0j71FblAv35IWUJ9VDH3lXaUX9MZYdIynTN7hW+oqPcRK5xls1Nw==", + "dependencies": { + "calendar": "^0.1.0", + "classnames": "^2.1.1", + "create-react-class": "^15.6.3", + "immutable": "^3.7.2", + "prop-types": "^15.6.0", + "react-addons-pure-render-mixin": "^15.6.2" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "moment": "^2.18.1", + "moment-range": "^3.0.3", + "react": "0.14.x || 15.x.x || 16.x.x", + "react-dom": "0.14.x || 15.x.x || 16.x.x" + } + }, + "node_modules/react-daterange-picker/node_modules/immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dnd": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-15.1.1.tgz", + "integrity": "sha512-QLrHtPU08U4c5zop0ANeqrHXaQw2EWLMn8DQoN6/e4eSN/UbB84P49/80Qg0MEF29VLB5vikSoiFh9N8ASNmpQ==", + "dependencies": { + "@react-dnd/invariant": "3.0.0", + "@react-dnd/shallowequal": "3.0.0", + "dnd-core": "15.1.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-15.1.2.tgz", + "integrity": "sha512-mem9QbutUF+aA2YC1y47G3ECjnYV/sCYKSnu5Jd7cbg3fLMPAwbnTf/JayYdnCH5l3eg9akD9dQt+cD0UdF8QQ==", + "dependencies": { + "dnd-core": "15.1.1" + } + }, + "node_modules/react-docgen": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-5.4.0.tgz", + "integrity": "sha512-JBjVQ9cahmNlfjMGxWUxJg919xBBKAoy3hgDgKERbR+BcF4ANpDuzWAScC7j27hZfd8sJNmMPOLWo9+vB/XJEQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@babel/generator": "^7.12.11", + "@babel/runtime": "^7.7.6", + "ast-types": "^0.14.2", + "commander": "^2.19.0", + "doctrine": "^3.0.0", + "estree-to-babel": "^3.1.0", + "neo-async": "^2.6.1", + "node-dir": "^0.1.10", + "strip-indent": "^3.0.0" + }, + "bin": { + "react-docgen": "bin/react-docgen.js" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/react-docgen-typescript": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", + "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", + "dev": true, + "peerDependencies": { + "typescript": ">= 4.3.x" + } + }, + "node_modules/react-docgen/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/react-docgen/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + }, + "peerDependencies": { + "react": "^16.14.0" + } + }, + "node_modules/react-draggable": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.4.tgz", + "integrity": "sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==", + "dependencies": { + "clsx": "^1.1.1", + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, + "node_modules/react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==", + "dev": true + }, + "node_modules/react-google-recaptcha": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-1.1.0.tgz", + "integrity": "sha512-GMWZEsIKyBVG+iXfVMwtMVKFJATu5c+oguL/5i95H3Jb5d5CG4DY0W9t4QhdSSulgkXbZMgv0VSuGF/GV1ENTA==", + "dependencies": { + "prop-types": "^15.5.0", + "react-async-script": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, + "node_modules/react-helmet-async": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.2.3.tgz", + "integrity": "sha512-mCk2silF53Tq/YaYdkl2sB+/tDoPnaxN7dFS/6ZLJb/rhUY2EWGI5Xj2b4jHppScMqY45MbgPSwTxDchKpZ5Kw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.2.0", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0", + "react-dom": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/react-highlight": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-highlight/-/react-highlight-0.14.0.tgz", + "integrity": "sha512-kWE+KXOXidS7SABhVopOgMnowbI3RAfeGZbnrduLNlWrYAED8sycL9l/Fvw3w0PFpIIawB7mRDnyhDcM/cIIGA==", + "dependencies": { + "highlight.js": "^10.5.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "dependencies": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^16.3.0 || ^15.5.4", + "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" + } + }, + "node_modules/react-lazyload": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-lazyload/-/react-lazyload-3.2.0.tgz", + "integrity": "sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw==", + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0", + "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-onclickoutside": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz", + "integrity": "sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q==", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" + }, + "peerDependencies": { + "react": "^15.5.x || ^16.x || ^17.x", + "react-dom": "^15.5.x || ^16.x || ^17.x" + } + }, + "node_modules/react-popper": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz", + "integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "@hypnosphi/create-react-context": "^0.3.1", + "deep-equal": "^1.1.1", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + }, + "peerDependencies": { + "react": "0.14.x || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/react-popper-tooltip": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz", + "integrity": "sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@popperjs/core": "^2.5.4", + "react-popper": "^2.2.4" + }, + "peerDependencies": { + "react": "^16.6.0 || ^17.0.0", + "react-dom": "^16.6.0 || ^17.0.0" + } + }, + "node_modules/react-popper-tooltip/node_modules/react-popper": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.5.tgz", + "integrity": "sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw==", + "dev": true, + "dependencies": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "react": "^16.8.0 || ^17" + } + }, + "node_modules/react-redux": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.2.tgz", + "integrity": "sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "hoist-non-react-statics": "^3.3.0", + "invariant": "^2.2.4", + "loose-envify": "^1.1.0", + "prop-types": "^15.6.1", + "react-is": "^16.6.0", + "react-lifecycles-compat": "^3.0.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0-0 || ^16.0.0-0", + "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0" + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-resize-detector": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-2.3.0.tgz", + "integrity": "sha512-oCAddEWWeFWYH5FAcHdBYcZjAw9fMzRUK9sWSx6WvSSOPVRxcHd5zTIGy/mOus+AhN/u6T4TMiWxvq79PywnJQ==", + "dependencies": { + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1", + "prop-types": "^15.6.0", + "resize-observer-polyfill": "^1.5.0" + }, + "peerDependencies": { + "react": "^0.14.7 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/react-router": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", + "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", + "dependencies": { + "history": "^4.7.2", + "hoist-non-react-statics": "^2.5.0", + "invariant": "^2.2.4", + "loose-envify": "^1.3.1", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.1", + "warning": "^4.0.1" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-dom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", + "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", + "dependencies": { + "history": "^4.7.2", + "invariant": "^2.2.4", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.1", + "react-router": "^4.3.1", + "warning": "^4.0.1" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/react-router-dom/node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/react-router/node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, + "node_modules/react-router/node_modules/hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, + "node_modules/react-router/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/react-router/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/react-sizeme": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/react-sizeme/-/react-sizeme-3.0.2.tgz", + "integrity": "sha512-xOIAOqqSSmKlKFJLO3inBQBdymzDuXx4iuwkNcJmC96jeiOg5ojByvL+g3MW9LPEsojLbC6pf68zOfobK8IPlw==", + "dev": true, + "dependencies": { + "element-resize-detector": "^1.2.2", + "invariant": "^2.2.4", + "shallowequal": "^1.1.0", + "throttle-debounce": "^3.0.1" + } + }, + "node_modules/react-smooth": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-1.0.6.tgz", + "integrity": "sha512-B2vL4trGpNSMSOzFiAul9kFAsxTukL9Wyy9EXtkQy3GJr6sZqW9e1nShdVOJ3hRYamPZ94O17r3Q0bjSw3UYtg==", + "dependencies": { + "lodash": "~4.17.4", + "prop-types": "^15.6.0", + "raf": "^3.4.0", + "react-transition-group": "^2.5.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0", + "react-dom": "^15.0.0 || ^16.0.0" + } + }, + "node_modules/react-smooth/node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/react-smooth/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, + "node_modules/react-syntax-highlighter": { + "version": "13.5.3", + "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.3.tgz", + "integrity": "sha512-crPaF+QGPeHNIblxxCdf2Lg936NAHKhNhuMzRL3F9ct6aYXL3NcZtCL0Rms9+qVo6Y1EQLdXGypBNSbPL/r+qg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.3.1", + "highlight.js": "^10.1.1", + "lowlight": "^1.14.0", + "prismjs": "^1.21.0", + "refractor": "^3.1.0" + }, + "peerDependencies": { + "react": ">= 0.14.0" + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz", + "integrity": "sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ==", + "dependencies": { + "@babel/runtime": "^7.10.2", + "use-composed-ref": "^1.0.0", + "use-latest": "^1.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/react-tippy": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/react-tippy/-/react-tippy-1.4.0.tgz", + "integrity": "sha512-r/hM5XK9Ztr2ZY7IWKuRmISTlUPS/R6ddz6PO2EuxCgW+4JBcGZRPU06XcVPRDCOIiio8ryBQFrXMhFMhsuaHA==", + "dependencies": { + "popper.js": "^1.11.1" + } + }, + "node_modules/react-toastify": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-5.5.0.tgz", + "integrity": "sha512-jsVme7jALIFGRyQsri/g4YTsRuaaGI70T6/ikjwZMB4mwTZaCWqj5NqxhGrRStKlJc5npXKKvKeqTiRGQl78LQ==", + "dependencies": { + "@babel/runtime": "^7.4.2", + "classnames": "^2.2.6", + "prop-types": "^15.7.2", + "react-transition-group": "^4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-virtualized": { + "version": "9.22.3", + "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.3.tgz", + "integrity": "sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "clsx": "^1.0.4", + "dom-helpers": "^5.1.3", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": "^15.3.0 || ^16.0.0-alpha", + "react-dom": "^15.3.0 || ^16.0.0-alpha" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-1.8.5.tgz", + "integrity": "sha512-tM9mprJbXVEBxjM7zHsIy6Cc41oO/pVYqyAsOHLxlJrbNBuLs0PHB3iys2M+RqCF0//k8nJtZF6X6swSkWY3tg==", + "dependencies": { + "classnames": "^2.2.5", + "core-js": "^2.6.10", + "d3-interpolate": "^1.3.0", + "d3-scale": "^2.1.0", + "d3-shape": "^1.2.0", + "lodash": "^4.17.5", + "prop-types": "^15.6.0", + "react-resize-detector": "^2.3.0", + "react-smooth": "^1.0.5", + "recharts-scale": "^0.4.2", + "reduce-css-calc": "^1.3.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0", + "react-dom": "^15.0.0 || ^16.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.", + "hasInstallScript": true + }, + "node_modules/reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dependencies": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + } + }, + "node_modules/reduce-css-calc/node_modules/balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" + }, + "node_modules/reduce-function-call": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.3.tgz", + "integrity": "sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz", + "integrity": "sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=", + "peerDependencies": { + "immutable": "^3.8.1 || ^4.0.0-rc.1" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", + "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", + "peerDependencies": { + "redux": "^4" + } + }, + "node_modules/refractor": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "dev": true, + "dependencies": { + "hastscript": "^6.0.0", + "parse-entities": "^2.0.0", + "prismjs": "~1.27.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "node_modules/regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz", + "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remark-footnotes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/remark-footnotes/-/remark-footnotes-2.0.0.tgz", + "integrity": "sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-1.6.22.tgz", + "integrity": "sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ==", + "dev": true, + "dependencies": { + "@babel/core": "7.12.9", + "@babel/helper-plugin-utils": "7.10.4", + "@babel/plugin-proposal-object-rest-spread": "7.12.1", + "@babel/plugin-syntax-jsx": "7.12.1", + "@mdx-js/util": "1.6.22", + "is-alphabetical": "1.0.4", + "remark-parse": "8.0.3", + "unified": "9.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx/node_modules/@babel/core": { + "version": "7.12.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz", + "integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.12.5", + "@babel/helper-module-transforms": "^7.12.1", + "@babel/helpers": "^7.12.5", + "@babel/parser": "^7.12.7", + "@babel/template": "^7.12.7", + "@babel/traverse": "^7.12.9", + "@babel/types": "^7.12.7", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/remark-mdx/node_modules/@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "node_modules/remark-mdx/node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", + "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.12.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/remark-mdx/node_modules/@babel/plugin-syntax-jsx": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz", + "integrity": "sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/remark-mdx/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/remark-mdx/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remark-parse": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.3.tgz", + "integrity": "sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q==", + "dev": true, + "dependencies": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-squeeze-paragraphs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz", + "integrity": "sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw==", + "dev": true, + "dependencies": { + "mdast-squeeze-paragraphs": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "node_modules/renderkid": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "dev": true, + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + } + }, + "node_modules/renderkid/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/renderkid/node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/css-what": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.0.1.tgz", + "integrity": "sha512-z93ZGFLNc6yaoXAmVhqoSIb+BduplteCt1fepvwhBUQK6MNE4g6fgjpuZKJKp0esUe+vXWlIkwZZjNWoOKw0ZA==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/renderkid/node_modules/dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/renderkid/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/renderkid/node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/renderkid/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rtcpeerconnection-shim": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz", + "integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==", + "dependencies": { + "sdp": "^2.6.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "dependencies": { + "aproba": "^1.1.1" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", + "dev": true + }, + "node_modules/scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/sdp": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz", + "integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw==" + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "node_modules/selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "dependencies": { + "node-forge": "^0.10.0" + } + }, + "node_modules/semantic-ui-react": { + "version": "0.87.3", + "resolved": "https://registry.npmjs.org/semantic-ui-react/-/semantic-ui-react-0.87.3.tgz", + "integrity": "sha512-YJgFYEheeFBMm/epZpIpWKF9glgSShdLPiY8zoUi+KJ0IKtLtbI8RbMD/ELbZkY+SO/IWbK/f/86pWt3PVvMVA==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "@semantic-ui-react/event-stack": "^3.1.0", + "classnames": "^2.2.6", + "keyboard-key": "^1.0.4", + "lodash": "^4.17.11", + "prop-types": "^15.6.2", + "react-is": "^16.7.0", + "react-popper": "^1.3.3", + "shallowequal": "^1.1.0" + }, + "peerDependencies": { + "react": "^16.3.0", + "react-dom": "^16.3.0" + } + }, + "node_modules/semantic-ui-react/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-favicon": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", + "integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=", + "dev": true, + "dependencies": { + "etag": "~1.8.1", + "fresh": "0.5.2", + "ms": "2.1.1", + "parseurl": "~1.3.2", + "safe-buffer": "5.1.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-favicon/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "node_modules/serve-favicon/node_modules/safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" + }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, + "node_modules/shapefile": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/shapefile/-/shapefile-0.3.1.tgz", + "integrity": "sha1-m7mkKb1ghqDPsDli0Uz99CD/uhI=", + "dependencies": { + "d3-queue": "1", + "iconv-lite": "0.2", + "optimist": "0.3" + }, + "bin": { + "dbfcat": "bin/dbfcat", + "shp2json": "bin/shp2json", + "shpcat": "bin/shpcat" + } + }, + "node_modules/shapefile/node_modules/d3-queue": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-1.2.3.tgz", + "integrity": "sha1-FDpwHPpl/gISkvMhwQ0U6Yq9SRs=" + }, + "node_modules/shapefile/node_modules/iconv-lite": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/snapdragon/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/socket.io-client": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.4.1.tgz", + "integrity": "sha512-N5C/L5fLNha5Ojd7Yeb/puKcPWWcoB/A09fEjjNsg91EDVr5twk/OEyO6VT9dlLSUNY85NpW6KBhVMvaLKQ3vQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.0.0", + "backo2": "~1.0.2", + "debug": "~4.3.2", + "engine.io-client": "~6.1.1", + "parseuri": "0.0.6", + "socket.io-parser": "~4.1.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz", + "integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==", + "dependencies": { + "@socket.io/component-emitter": "~3.0.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs-client": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz", + "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^1.1.0", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "dependencies": { + "is-plain-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/spdy-transport/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/spdy-transport/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/spdy-transport/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "dev": true, + "dependencies": { + "figgy-pudding": "^3.5.1" + } + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/stackframe": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.1.tgz", + "integrity": "sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==", + "dev": true + }, + "node_modules/state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/static-eval": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.0.tgz", + "integrity": "sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw==", + "dependencies": { + "escodegen": "^1.11.1" + } + }, + "node_modules/static-eval/node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/static-eval/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/static-eval/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/static-extend/node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-module": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/static-module/-/static-module-2.2.5.tgz", + "integrity": "sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ==", + "dependencies": { + "concat-stream": "~1.6.0", + "convert-source-map": "^1.5.1", + "duplexer2": "~0.1.4", + "escodegen": "~1.9.0", + "falafel": "^2.1.0", + "has": "^1.0.1", + "magic-string": "^0.22.4", + "merge-source-map": "1.0.4", + "object-inspect": "~1.4.0", + "quote-stream": "~1.0.2", + "readable-stream": "~2.3.3", + "shallow-copy": "~0.0.1", + "static-eval": "^2.0.0", + "through2": "~2.0.3" + } + }, + "node_modules/static-module/node_modules/object-inspect": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.4.1.tgz", + "integrity": "sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==" + }, + "node_modules/static-module/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/static-module/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/static-module/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/store2": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/store2/-/store2-2.13.2.tgz", + "integrity": "sha512-CMtO2Uneg3SAz/d6fZ/6qbqqQHi2ynq6/KzMD/26gTkiEShCcpqFfTHgOxsE0egAq6SX3FmN4CeSqn8BzXQkJg==", + "dev": true + }, + "node_modules/stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "dependencies": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-browserify/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/stream-http/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-http/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "node_modules/strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.1", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", + "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.padstart": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padstart/-/string.prototype.padstart-3.1.3.tgz", + "integrity": "sha512-NZydyOMtYxpTjGqp0VN5PYUF/tsU15yDMZnUdj16qRUIUiMJkHHSDElYyQFrMu+/WloTpA7MQSiADhBicDfaoA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "bin": { + "strip-json-comments": "cli.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true + }, + "node_modules/style-loader": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "dev": true, + "dependencies": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + }, + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/style-loader/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/style-loader/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/style-loader/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/style-to-object": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz", + "integrity": "sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA==", + "dev": true, + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dev": true, + "dependencies": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/sugarss": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", + "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/symbol.prototype.description": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/symbol.prototype.description/-/symbol.prototype.description-1.0.5.tgz", + "integrity": "sha512-x738iXRYsrAt9WBhRCVG5BtIC3B7CUkFwbHW2zOvGtwM33s7JjrCDyq8V0zgMYVb5ymsL8+qkzzpANH63CPQaQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-symbol-description": "^1.0.0", + "has-symbols": "^1.0.2", + "object.getownpropertydescriptors": "^2.1.2" + }, + "engines": { + "node": ">= 0.11.15" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synchronous-promise": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.15.tgz", + "integrity": "sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg==", + "dev": true + }, + "node_modules/syncod": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/syncod/-/syncod-0.0.1.tgz", + "integrity": "sha512-KHDsGQ4UcP+wSMaqH7wjH4DHxeHKRlmEO5jlSVCS+0x9xA4ZhdKYg/ameGF7RXaFDUcsti6Zj5s5W1Z4/YsbHA==" + }, + "node_modules/tailwindcss": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-1.9.6.tgz", + "integrity": "sha512-nY8WYM/RLPqGsPEGEV2z63riyQPcHYZUJpAwdyBzVpxQHOHqHE+F/fvbCeXhdF1+TA5l72vSkZrtYCB9hRcwkQ==", + "dependencies": { + "@fullhuman/postcss-purgecss": "^2.1.2", + "autoprefixer": "^9.4.5", + "browserslist": "^4.12.0", + "bytes": "^3.0.0", + "chalk": "^3.0.0 || ^4.0.0", + "color": "^3.1.2", + "detective": "^5.2.0", + "fs-extra": "^8.0.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.20", + "node-emoji": "^1.8.1", + "normalize.css": "^8.0.1", + "object-hash": "^2.0.3", + "postcss": "^7.0.11", + "postcss-functions": "^3.0.0", + "postcss-js": "^2.0.0", + "postcss-nested": "^4.1.1", + "postcss-selector-parser": "^6.0.0", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "reduce-css-calc": "^2.1.6", + "resolve": "^1.14.2" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/tailwindcss/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/tailwindcss/node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/tailwindcss/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/tailwindcss/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/tailwindcss/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/tailwindcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/tailwindcss/node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "node_modules/tailwindcss/node_modules/reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dependencies": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/tailwindcss/node_modules/reduce-css-calc/node_modules/postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + }, + "node_modules/tailwindcss/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tar/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/telejson": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/telejson/-/telejson-5.3.3.tgz", + "integrity": "sha512-PjqkJZpzEggA9TBpVtJi1LVptP7tYtXB6rEubwlHap76AMjzvOdKX41CxyaW7ahhzDU1aftXnMCx5kAPDZTQBA==", + "dev": true, + "dependencies": { + "@types/is-function": "^1.0.0", + "global": "^4.4.0", + "is-function": "^1.0.2", + "is-regex": "^1.1.2", + "is-symbol": "^1.0.3", + "isobject": "^4.0.0", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3" + } + }, + "node_modules/telejson/node_modules/isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", + "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", + "dev": true, + "dependencies": { + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz", + "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==", + "dev": true, + "dependencies": { + "cacache": "^15.0.5", + "find-cache-dir": "^3.3.1", + "jest-worker": "^26.5.0", + "p-limit": "^3.0.2", + "schema-utils": "^3.0.0", + "serialize-javascript": "^5.0.1", + "source-map": "^0.6.1", + "terser": "^5.3.4", + "webpack-sources": "^1.4.3" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/terser-webpack-plugin/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser-webpack-plugin/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser-webpack-plugin/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser-webpack-plugin/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/terser/node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "dependencies": { + "setimmediate": "^1.0.4" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tiny-invariant": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", + "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, + "node_modules/to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/topojson": { + "version": "1.6.27", + "resolved": "https://registry.npmjs.org/topojson/-/topojson-1.6.27.tgz", + "integrity": "sha1-rb4zpn4vFnPTON8SZErSD8ILQu0=", + "deprecated": "Use topojson-client, topojson-server or topojson-simplify directly.", + "dependencies": { + "d3": "3", + "d3-geo-projection": "0.2", + "d3-queue": "2", + "optimist": "0.3", + "rw": "1", + "shapefile": "0.3" + }, + "bin": { + "topojson": "bin/topojson", + "topojson-geojson": "bin/topojson-geojson", + "topojson-group": "bin/topojson-group", + "topojson-merge": "bin/topojson-merge", + "topojson-svg": "bin/topojson-svg" + } + }, + "node_modules/toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "node_modules/trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", + "dev": true + }, + "node_modules/trim-trailing-lines": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz", + "integrity": "sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true + }, + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.3.0.tgz", + "integrity": "sha512-MgGly4I6cStsJy27ViE32UoqxPTN9Xly4anxxVyaIWR+9BGxboV4EyJBGfR3RePV7Ksjj3rHmPZJeIt+7o4Vag==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "*" + } + }, + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-loader/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-css-modules": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/typed-css-modules/-/typed-css-modules-0.7.0.tgz", + "integrity": "sha512-eNaAHKiao0ON0tkLE8ITma9Fx9MUDQjJwL+P9iCinGAIHSt2XKFVSx0A6GWLrbX/yNQjzzUmuJFikobSymZXng==", + "dev": true, + "dependencies": { + "@types/css-modules-loader-core": "^1.1.0", + "camelcase": "^5.3.1", + "chalk": "^2.1.0", + "chokidar": "^3.4.0", + "css-modules-loader-core": "^1.1.0", + "glob": "^7.1.2", + "is-there": "^4.4.2", + "mkdirp": "^1.0.0", + "yargs": "^15.4.1" + }, + "bin": { + "tcm": "lib/cli.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/typed-css-modules/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/typed-css-modules/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/typed-css-modules/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/typed-css-modules/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typed-css-modules/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typed-css-modules/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typed-css-modules/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typed-css-modules/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typed-css-modules/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typed-css-modules/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/typed-styles": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", + "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==" + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz", + "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/uglify-js": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz", + "integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", + "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", + "dev": true + }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "dev": true + }, + "node_modules/unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", + "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", + "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.0.tgz", + "integrity": "sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg==", + "dev": true, + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/union-value/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/unist-builder": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-2.0.3.tgz", + "integrity": "sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-generated": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-1.1.6.tgz", + "integrity": "sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-3.1.0.tgz", + "integrity": "sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-2.1.0.tgz", + "integrity": "sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q==", + "dev": true, + "dependencies": { + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "dev": true, + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", + "dev": true + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/use-composed-ref": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.2.1.tgz", + "integrity": "sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz", + "integrity": "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.0.tgz", + "integrity": "sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==", + "dependencies": { + "use-isomorphic-layout-effect": "^1.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.2.0.tgz", + "integrity": "sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vlq": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==" + }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", + "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "dependencies": { + "chokidar": "^2.1.8" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "optional": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "optional": true + }, + "node_modules/watchpack-chokidar2/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "optional": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/watchpack-chokidar2/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/watchpack-chokidar2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/watchpack-chokidar2/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "optional": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/web-encoding": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", + "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", + "dev": true, + "dependencies": { + "util": "^0.12.3" + }, + "optionalDependencies": { + "@zxing/text-encoding": "0.9.0" + } + }, + "node_modules/web-namespaces": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz", + "integrity": "sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "node_modules/webpack": { + "version": "4.46.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + }, + "webpack-command": { + "optional": true + } + } + }, + "node_modules/webpack-bundle-analyzer": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz", + "integrity": "sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.19", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 6.14.4" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-cli": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz", + "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "cross-spawn": "^6.0.5", + "enhanced-resolve": "^4.1.1", + "findup-sync": "^3.0.0", + "global-modules": "^2.0.0", + "import-local": "^2.0.0", + "interpret": "^1.4.0", + "loader-utils": "^1.4.0", + "supports-color": "^6.1.0", + "v8-compile-cache": "^2.1.1", + "yargs": "^13.3.2" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=6.11.5" + }, + "peerDependencies": { + "webpack": "4.x.x" + } + }, + "node_modules/webpack-cli/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/webpack-cli/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/webpack-cli/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/webpack-cli/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/webpack-cli/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-cli/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/webpack-cli/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack-cli/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-cli/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-cli/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/webpack-cli/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-cli/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-cli/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/webpack-cli/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-cli/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/webpack-cli/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dev": true, + "dependencies": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/webpack-dev-middleware/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/webpack-dev-middleware/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/webpack-dev-server": { + "version": "3.11.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", + "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==", + "dev": true, + "dependencies": { + "ansi-html-community": "0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 6.11.5" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/webpack-dev-server/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack-dev-server/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "node_modules/webpack-dev-server/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack-dev-server/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/string-width/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-dev-server/node_modules/url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/webpack-dev-server/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/webpack-filter-warnings-plugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/webpack-filter-warnings-plugin/-/webpack-filter-warnings-plugin-1.2.1.tgz", + "integrity": "sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==", + "dev": true, + "engines": { + "node": ">= 4.3 < 5.0.0 || >= 5.10" + }, + "peerDependencies": { + "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + } + }, + "node_modules/webpack-hot-middleware": { + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.1.tgz", + "integrity": "sha512-Koh0KyU/RPYwel/khxbsDz9ibDivmUbrRuKSSQvW42KSDdO4w23WI3SkHpSUKHE76LrFnnM/L7JCrpBwu8AXYw==", + "dev": true, + "dependencies": { + "ansi-html-community": "0.0.8", + "html-entities": "^2.1.0", + "querystring": "^0.2.0", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "dependencies": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/webpack-log/node_modules/ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/webpack-sources/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.2.2.tgz", + "integrity": "sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA==", + "dev": true, + "dependencies": { + "debug": "^3.0.0" + } + }, + "node_modules/webpack-virtual-modules/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/webpack/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/webpack/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/webpack/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/webpack/node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/webpack/node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/webpack/node_modules/loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webpack/node_modules/memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "dependencies": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "node_modules/webpack/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "dependencies": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/webpack/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/webpack/node_modules/terser": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", + "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "dev": true, + "dependencies": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/webpack/node_modules/terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dev": true, + "dependencies": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/webpack/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webpack/node_modules/watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + }, + "optionalDependencies": { + "chokidar": "^3.4.1", + "watchpack-chokidar2": "^2.0.1" + } + }, + "node_modules/webrtc-adapter": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz", + "integrity": "sha512-TbrbBmiQBL9n0/5bvDdORc6ZfRY/Z7JnEj+EYOD1ghseZdpJ+nF2yx14k3LgQKc7JZnG7HAcL+zHnY25So9d7A==", + "dependencies": { + "rtcpeerconnection-shim": "^1.2.15", + "sdp": "^2.12.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/winston": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz", + "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==", + "dev": true, + "dependencies": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "dev": true, + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston-transport/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/winston-transport/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/winston/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/winston/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "dependencies": { + "errno": "~0.1.7" + } + }, + "node_modules/worker-rpc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz", + "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==", + "dev": true, + "dependencies": { + "microevent.ts": "~0.1.1" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", + "dev": true + }, + "node_modules/xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "node_modules/xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", + "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", "dev": true, "requires": { + "@jridgewell/trace-mapping": "^0.3.0" + } + }, + "@babel/cli": { + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.17.6.tgz", + "integrity": "sha512-l4w608nsDNlxZhiJ5tE3DbNmr61fIKMZ6fTBo171VEFuFMIYuJ3mHRhTLEkKKyvx2Mizkkv/0a8OJOnZqkKYNA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.4", "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", "chokidar": "^3.4.0", "commander": "^4.0.1", @@ -39,49 +26112,41 @@ } }, "@babel/compat-data": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.8.tgz", - "integrity": "sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.7.tgz", + "integrity": "sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==", "dev": true }, "@babel/core": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.12.tgz", - "integrity": "sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.8.tgz", + "integrity": "sha512-OdQDV/7cRBtJHLSOBqqbYNkOcydOgnX59TZx4puf41fzcVtN3e/4yqY8lMQsK+5X2lJtAdmA+6OHqsj1hBJ4IQ==", "dev": true, "requires": { + "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", - "@babel/helper-compilation-targets": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", - "@babel/helpers": "^7.16.7", - "@babel/parser": "^7.16.12", + "@babel/generator": "^7.17.7", + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-module-transforms": "^7.17.7", + "@babel/helpers": "^7.17.8", + "@babel/parser": "^7.17.8", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.10", - "@babel/types": "^7.16.8", + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "semver": "^6.3.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "semver": "^6.3.0" } }, "@babel/generator": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz", - "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", "dev": true, "requires": { - "@babel/types": "^7.16.8", + "@babel/types": "^7.17.0", "jsesc": "^2.5.1", "source-map": "^0.5.0" }, @@ -114,21 +26179,21 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz", - "integrity": "sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz", + "integrity": "sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==", "dev": true, "requires": { - "@babel/compat-data": "^7.16.4", + "@babel/compat-data": "^7.17.7", "@babel/helper-validator-option": "^7.16.7", "browserslist": "^4.17.5", "semver": "^6.3.0" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.10.tgz", - "integrity": "sha512-wDeej0pu3WN/ffTxMNCPW5UCiOav8IcLRxSIyp/9+IF2xJUM9h/OYjg0IJLHaL6F8oU8kqMz9nc1vryXhMsgXg==", + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz", + "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", @@ -141,13 +26206,13 @@ } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz", - "integrity": "sha512-fk5A6ymfp+O5+p2yCkXAu5Kyj6v0xh0RBeNcAkYUMDvvAAoxvSKXn+Jb37t/yWFiQVDFK1ELpUTD8/aLhCPu+g==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz", + "integrity": "sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", - "regexpu-core": "^4.7.1" + "regexpu-core": "^5.0.1" } }, "@babel/helper-define-polyfill-provider": { @@ -214,12 +26279,12 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz", - "integrity": "sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz", + "integrity": "sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.17.0" } }, "@babel/helper-module-imports": { @@ -232,19 +26297,19 @@ } }, "@babel/helper-module-transforms": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz", - "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz", + "integrity": "sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", "@babel/helper-split-export-declaration": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" } }, "@babel/helper-optimise-call-expression": { @@ -287,12 +26352,12 @@ } }, "@babel/helper-simple-access": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz", - "integrity": "sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", + "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", "dev": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.17.0" } }, "@babel/helper-skip-transparent-expression-wrappers": { @@ -338,14 +26403,14 @@ } }, "@babel/helpers": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz", - "integrity": "sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", + "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", "dev": true, "requires": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/traverse": "^7.17.3", + "@babel/types": "^7.17.0" } }, "@babel/highlight": { @@ -360,9 +26425,9 @@ } }, "@babel/parser": { - "version": "7.16.12", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz", - "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.8.tgz", + "integrity": "sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -407,25 +26472,27 @@ } }, "@babel/plugin-proposal-class-static-block": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz", - "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==", + "version": "7.17.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz", + "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.17.6", "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, "@babel/plugin-proposal-decorators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.16.7.tgz", - "integrity": "sha512-DoEpnuXK14XV9btI1k8tzNGCutMclpj4yru8aXKoHlVmbO1s+2A+g2+h4JhcjrxkFJqzbymnLG6j/niOf3iFXQ==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.8.tgz", + "integrity": "sha512-U69odN4Umyyx1xO1rTII0IDkAEC+RNlcKXtqOblfpzqy1C+aOplb76BQNq0+XdpVkOaPlpEDwd++joY8FNFJKA==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.16.7", + "@babel/helper-create-class-features-plugin": "^7.17.6", "@babel/helper-plugin-utils": "^7.16.7", - "@babel/plugin-syntax-decorators": "^7.16.7" + "@babel/helper-replace-supers": "^7.16.7", + "@babel/plugin-syntax-decorators": "^7.17.0", + "charcodes": "^0.2.0" } }, "@babel/plugin-proposal-dynamic-import": { @@ -499,12 +26566,12 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz", - "integrity": "sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==", + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz", + "integrity": "sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==", "dev": true, "requires": { - "@babel/compat-data": "^7.16.4", + "@babel/compat-data": "^7.17.0", "@babel/helper-compilation-targets": "^7.16.7", "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", @@ -601,9 +26668,9 @@ } }, "@babel/plugin-syntax-decorators": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.16.7.tgz", - "integrity": "sha512-vQ+PxL+srA7g6Rx6I1e15m55gftknl2X8GCUW1JTlkTaXZLJOS0UcaY0eK9jYT7IYf4awn6qwyghVHLDz1WyMw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.0.tgz", + "integrity": "sha512-qWe85yCXsvDEluNP0OyeQjH63DlhAR3W7K9BxxU1MvbDb48tgBG+Ao6IJJ6smPDrrVzSQZrbF6donpkFBMcs3A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -808,9 +26875,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz", - "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz", + "integrity": "sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.16.7" @@ -905,25 +26972,25 @@ } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz", - "integrity": "sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.7.tgz", + "integrity": "sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", - "@babel/helper-simple-access": "^7.16.7", + "@babel/helper-simple-access": "^7.17.7", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz", - "integrity": "sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz", + "integrity": "sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-module-transforms": "^7.16.7", + "@babel/helper-module-transforms": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7", "babel-plugin-dynamic-import-node": "^2.3.3" @@ -995,16 +27062,16 @@ } }, "@babel/plugin-transform-react-jsx": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz", - "integrity": "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==", + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz", + "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", "@babel/helper-plugin-utils": "^7.16.7", "@babel/plugin-syntax-jsx": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/types": "^7.17.0" } }, "@babel/plugin-transform-react-jsx-development": { @@ -1252,30 +27319,30 @@ } }, "@babel/register": { - "version": "7.16.9", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.16.9.tgz", - "integrity": "sha512-jJ72wcghdRIlENfvALcyODhNoGE5j75cYHdC+aQMh6cU/P86tiiXTp9XYZct1UxUMo/4+BgQRyNZEGx0KWGS+g==", + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.17.7.tgz", + "integrity": "sha512-fg56SwvXRifootQEDQAu1mKdjh5uthPzdO0N6t358FktfL4XjAVXuH58ULoiW8mesxiOgNIrxiImqEwv0+hRRA==", "dev": true, "requires": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", "make-dir": "^2.1.0", - "pirates": "^4.0.0", + "pirates": "^4.0.5", "source-map-support": "^0.5.16" } }, "@babel/runtime": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", - "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz", + "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz", - "integrity": "sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg==", + "version": "7.17.8", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.8.tgz", + "integrity": "sha512-ZbYSUvoSF6dXZmMl/CYTMOvzIFnbGfv4W3SEHYgMvNsFTeLaF2gkGAF4K2ddmtSK4Emej+0aYcnSC6N5dPCXUQ==", "dev": true, "requires": { "core-js-pure": "^3.20.2", @@ -1294,27 +27361,27 @@ } }, "@babel/traverse": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz", - "integrity": "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==", + "version": "7.17.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", + "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.16.8", + "@babel/generator": "^7.17.3", "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-function-name": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.16.10", - "@babel/types": "^7.16.8", + "@babel/parser": "^7.17.3", + "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.16.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz", - "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -1327,10 +27394,16 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true + }, "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", "dev": true, "requires": { "colorspace": "1.1.x", @@ -1339,9 +27412,9 @@ } }, "@discoveryjs/json-ext": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", - "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, "@emotion/cache": { @@ -1502,9 +27575,9 @@ } }, "@gar/promisify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz", - "integrity": "sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, "@hypnosphi/create-react-context": { @@ -1522,6 +27595,28 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@jridgewell/resolve-uri": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", + "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", + "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", + "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "@mdx-js/mdx": { "version": "1.6.22", "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-1.6.22.tgz", @@ -1654,9 +27749,9 @@ } }, "@npmcli/fs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.0.tgz", - "integrity": "sha512-VhP1qZLXcrXRIaPoqb4YA55JQxLNF3jNR4T55IdOJa3+IFJKNYHtPvtXx8slmeMavj37vCzCfrqQM1vWLsYKLA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", "dev": true, "requires": { "@gar/promisify": "^1.0.1", @@ -1744,11 +27839,26 @@ } }, "@popperjs/core": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz", - "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==", + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.4.tgz", + "integrity": "sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==", "dev": true }, + "@react-dnd/asap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.0.tgz", + "integrity": "sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==" + }, + "@react-dnd/invariant": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-3.0.0.tgz", + "integrity": "sha512-keberJRIqPX15IK3SWS/iO1t/kGETiL1oczKrDitAaMnQ+kpHf81l3MrRmFjvfqcnApE+izEvwM6GsyoIcpsVA==" + }, + "@react-dnd/shallowequal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-3.0.0.tgz", + "integrity": "sha512-1ELWQdJB2UrCXTKK5cCD9uGLLIwECLIEdttKA255owdpchtXohIjZBTlFJszwYi2ZKe2Do+QvUzsGyGCMNwbdw==" + }, "@semantic-ui-react/event-stack": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@semantic-ui-react/event-stack/-/event-stack-3.1.2.tgz", @@ -1826,18 +27936,18 @@ "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==" }, "@storybook/addons": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.14.tgz", - "integrity": "sha512-Snu42ejLyBAh6PWdlrdI72HKN1oKY7q0R9qEID2wk953WrqgGu4URakp14YLxghJCyKTSfGPs6LNZRRI6H5xgA==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.19.tgz", + "integrity": "sha512-QNyRYhpqmHV8oJxxTBdkRlLSbDFhpBvfvMfIrIT1UXb/eemdBZTaCGVvXZ9UixoEEI7f8VwAQ44IvkU5B1509w==", "dev": true, "requires": { - "@storybook/api": "6.4.14", - "@storybook/channels": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/core-events": "6.4.14", + "@storybook/api": "6.4.19", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/router": "6.4.14", - "@storybook/theming": "6.4.14", + "@storybook/router": "6.4.19", + "@storybook/theming": "6.4.19", "@types/webpack-env": "^1.16.0", "core-js": "^3.8.2", "global": "^4.4.0", @@ -1845,18 +27955,18 @@ } }, "@storybook/api": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.4.14.tgz", - "integrity": "sha512-GGGwB5+EquoausTXYx4dnLBBk2sOiS1Z58mDj0swBXCZdjfyUfLyxjxvvb/hl65ltufWP3IdmlKKaLiuARXNtw==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.4.19.tgz", + "integrity": "sha512-aDvea+NpQCBjpNp9YidO1Pr7fzzCp15FSdkG+2ihGQfv5raxrN+IIJnGUXecpe71nvlYiB+29UXBVK7AL0j51Q==", "dev": true, "requires": { - "@storybook/channels": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/core-events": "6.4.14", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/router": "6.4.14", + "@storybook/router": "6.4.19", "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.4.14", + "@storybook/theming": "6.4.19", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", @@ -1870,9 +27980,9 @@ } }, "@storybook/builder-webpack4": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.4.14.tgz", - "integrity": "sha512-hRzwdNNLxuyb0XPpvbTSkQuqG2frhog2SsjgPVXorsSMPr95owo9Nq9hp+TnywpvaR9lrPlESzhhv2sSR3blTw==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.4.19.tgz", + "integrity": "sha512-wxA6SMH11duc9D53aeVVBwrVRemFIoxHp/dOugkkg6ZZFAb4ZmWzf/ENc3vQIZdZpfNRi7IZIZEOfoHc994cmw==", "dev": true, "requires": { "@babel/core": "^7.12.10", @@ -1896,22 +28006,22 @@ "@babel/preset-env": "^7.12.11", "@babel/preset-react": "^7.12.10", "@babel/preset-typescript": "^7.12.7", - "@storybook/addons": "6.4.14", - "@storybook/api": "6.4.14", - "@storybook/channel-postmessage": "6.4.14", - "@storybook/channels": "6.4.14", - "@storybook/client-api": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/components": "6.4.14", - "@storybook/core-common": "6.4.14", - "@storybook/core-events": "6.4.14", - "@storybook/node-logger": "6.4.14", - "@storybook/preview-web": "6.4.14", - "@storybook/router": "6.4.14", + "@storybook/addons": "6.4.19", + "@storybook/api": "6.4.19", + "@storybook/channel-postmessage": "6.4.19", + "@storybook/channels": "6.4.19", + "@storybook/client-api": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/components": "6.4.19", + "@storybook/core-common": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/node-logger": "6.4.19", + "@storybook/preview-web": "6.4.19", + "@storybook/router": "6.4.19", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.14", - "@storybook/theming": "6.4.14", - "@storybook/ui": "6.4.14", + "@storybook/store": "6.4.19", + "@storybook/theming": "6.4.19", + "@storybook/ui": "6.4.19", "@types/node": "^14.0.10", "@types/webpack": "^4.41.26", "autoprefixer": "^9.8.6", @@ -2267,14 +28377,14 @@ } }, "@storybook/channel-postmessage": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.4.14.tgz", - "integrity": "sha512-z+fBi/eAAswELWOdlIFI9XXNjyxfguKyqKGSQ7qdz3eFyxeuWnxTa9aZsnLIXpPKY9QPydpBSJcIKUCdN6DbIg==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.4.19.tgz", + "integrity": "sha512-E5h/itFzQ/6M08LR4kqlgqqmeO3tmavI+nUAlZrkCrotpJFNMHE2i0PQHg0TkFJrRDpYcrwD+AjUW4IwdqrisQ==", "dev": true, "requires": { - "@storybook/channels": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/core-events": "6.4.14", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", "core-js": "^3.8.2", "global": "^4.4.0", "qs": "^6.10.0", @@ -2282,22 +28392,22 @@ } }, "@storybook/channel-websocket": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-6.4.14.tgz", - "integrity": "sha512-4Y6TDeYLzItGIaYKo3s6xxSmUF11j96dOX7n74ax45zcMhpp/XwG5i0FU1DtGb5PnhPxg+vJmKa1IgizzaWRYg==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-6.4.19.tgz", + "integrity": "sha512-cXKwQjIXttfdUyZlcHORelUmJ5nUKswsnCA/qy7IRWpZjD8yQJcNk1dYC+tTHDVqFgdRT89pL0hRRB1rlaaR8Q==", "dev": true, "requires": { - "@storybook/channels": "6.4.14", - "@storybook/client-logger": "6.4.14", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", "core-js": "^3.8.2", "global": "^4.4.0", "telejson": "^5.3.2" } }, "@storybook/channels": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.14.tgz", - "integrity": "sha512-3QOVxFG6ZAxDXCta1ie4SUPQ3s50yHeuZzVg6uPp+DcC1FrXeDFYBcU9t0j/jrSgbeKcnFHWxmRHNy1BRyWv/A==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.19.tgz", + "integrity": "sha512-EwyoncFvTfmIlfsy8jTfayCxo2XchPkZk/9txipugWSmc057HdklMKPLOHWP0z5hLH0IbVIKXzdNISABm36jwQ==", "dev": true, "requires": { "core-js": "^3.8.2", @@ -2306,18 +28416,18 @@ } }, "@storybook/client-api": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.4.14.tgz", - "integrity": "sha512-hqdgE0zKVhcqG/8t/veJRgjsOT076LeKxoA+w2Ga4iU+reIGui/GvLsjvyFFTyOMHVeo2Ze4LW63oTYKF/I5iQ==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.4.19.tgz", + "integrity": "sha512-OCrT5Um3FDvZnimQKwWtwsaI+5agPwq2i8YiqlofrI/NPMKp0I7DEkCGwE5IRD1Q8BIKqHcMo5tTmfYi0AxyOg==", "dev": true, "requires": { - "@storybook/addons": "6.4.14", - "@storybook/channel-postmessage": "6.4.14", - "@storybook/channels": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/core-events": "6.4.14", + "@storybook/addons": "6.4.19", + "@storybook/channel-postmessage": "6.4.19", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/store": "6.4.14", + "@storybook/store": "6.4.19", "@types/qs": "^6.9.5", "@types/webpack-env": "^1.16.0", "core-js": "^3.8.2", @@ -2334,9 +28444,9 @@ } }, "@storybook/client-logger": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.4.14.tgz", - "integrity": "sha512-4VmFWZxhpeiG5fDhfqAyQbCfXZSBKS4fNKf35ABWiHStZRDndxml8K5WFtmOmMvVzjrGQx8HesenYMawK6xo/Q==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.4.19.tgz", + "integrity": "sha512-zmg/2wyc9W3uZrvxaW4BfHcr40J0v7AGslqYXk9H+ERLVwIvrR4NhxQFaS6uITjBENyRDxwzfU3Va634WcmdDQ==", "dev": true, "requires": { "core-js": "^3.8.2", @@ -2344,15 +28454,15 @@ } }, "@storybook/components": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.4.14.tgz", - "integrity": "sha512-M7unerbOnvg+UN7qPxBCBWzK/boVdSSQxRiPAr1OL3M4OyEU8+TNPdQeAG0aF4zqtU0BrsDf4E85EznoMXUiFQ==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.4.19.tgz", + "integrity": "sha512-q/0V37YAJA7CNc+wSiiefeM9+3XVk8ixBNylY36QCGJgIeGQ5/79vPyUe6K4lLmsQwpmZsIq1s1Ad5+VbboeOA==", "dev": true, "requires": { "@popperjs/core": "^2.6.0", - "@storybook/client-logger": "6.4.14", + "@storybook/client-logger": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/theming": "6.4.14", + "@storybook/theming": "6.4.19", "@types/color-convert": "^2.0.0", "@types/overlayscrollbars": "^1.12.0", "@types/react-syntax-highlighter": "11.0.5", @@ -2376,31 +28486,31 @@ } }, "@storybook/core": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.4.14.tgz", - "integrity": "sha512-41WNDXKMZuCKnvbLBBYCd1+ip4uJ4AGeCOhmp/KZK7TgkitJ0JrvyRgnbpXR8bAMiOv2Hh9t9Vmi5D3QZ8COlg==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.4.19.tgz", + "integrity": "sha512-55LOQ/h/kf1jMhjN85t/pIEdIwWEG9yV7bdwv3niVvmoypCxyyjn9/QNK0RKYAeDSUtdm6FVoJ6k5CpxWz2d8w==", "dev": true, "requires": { - "@storybook/core-client": "6.4.14", - "@storybook/core-server": "6.4.14" + "@storybook/core-client": "6.4.19", + "@storybook/core-server": "6.4.19" } }, "@storybook/core-client": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-6.4.14.tgz", - "integrity": "sha512-e9pzKz52DVhmo8+sUEDvagwGKVqWZ6NQBIt3mBvd79/zXTPkFRnSVitOyYErqhgN1kuwocTg+2BigRr3H0qXaQ==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-6.4.19.tgz", + "integrity": "sha512-rQHRZjhArPleE7/S8ZUolgzwY+hC0smSKX/3PQxO2GcebDjnJj6+iSV3h+aSMHMmTdoCQvjYw9aBpT8scuRe+A==", "dev": true, "requires": { - "@storybook/addons": "6.4.14", - "@storybook/channel-postmessage": "6.4.14", - "@storybook/channel-websocket": "6.4.14", - "@storybook/client-api": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/core-events": "6.4.14", + "@storybook/addons": "6.4.19", + "@storybook/channel-postmessage": "6.4.19", + "@storybook/channel-websocket": "6.4.19", + "@storybook/client-api": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/preview-web": "6.4.14", - "@storybook/store": "6.4.14", - "@storybook/ui": "6.4.14", + "@storybook/preview-web": "6.4.19", + "@storybook/store": "6.4.19", + "@storybook/ui": "6.4.19", "airbnb-js-shims": "^2.2.1", "ansi-to-html": "^0.6.11", "core-js": "^3.8.2", @@ -2414,9 +28524,9 @@ } }, "@storybook/core-common": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-6.4.14.tgz", - "integrity": "sha512-7NRmtcY2INmobsmUUX4afO78RHpyQMO8vboy6H8HRtfcw6fy4zaHoCb7gZZfvvn8gtBWNmwip8I9XK5BpRrh3Q==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-6.4.19.tgz", + "integrity": "sha512-X1pJJkO48DFxl6iyEemIKqRkJ7j9/cBh3BRBUr+xZHXBvnD0GKDXIocwh0PjSxSC6XSu3UCQnqtKi3PbjRl8Dg==", "dev": true, "requires": { "@babel/core": "^7.12.10", @@ -2440,7 +28550,7 @@ "@babel/preset-react": "^7.12.10", "@babel/preset-typescript": "^7.12.7", "@babel/register": "^7.12.1", - "@storybook/node-logger": "6.4.14", + "@storybook/node-logger": "6.4.19", "@storybook/semver": "^7.3.2", "@types/node": "^14.0.10", "@types/pretty-hrtime": "^1.0.0", @@ -2539,31 +28649,31 @@ } }, "@storybook/core-events": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.4.14.tgz", - "integrity": "sha512-9QFltg2mxTDjMBfmVtFHtrAEPY/i0oVp2kVdTWo6g05cPffYKAjNUnUVjUl7yiqcQmdEcdqUUQ0ut3xgmcYi/A==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.4.19.tgz", + "integrity": "sha512-KICzUw6XVQUJzFSCXfvhfHAuyhn4Q5J4IZEfuZkcGJS4ODkrO6tmpdYE5Cfr+so95Nfp0ErWiLUuodBsW9/rtA==", "dev": true, "requires": { "core-js": "^3.8.2" } }, "@storybook/core-server": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.4.14.tgz", - "integrity": "sha512-SzO8SaLTZ36Q4PNhJD4XJjlnonbR2Os0gzTknDBbwyIRPUtFUdk6isSG14RM5yYWPM0QQIs9og5ztSPX58YZlw==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.4.19.tgz", + "integrity": "sha512-bKsUB9f7hl5ya2JXxpIrErmbDQjoH39FVbzYZWjMo4t/b7+Xyi6vYadwyWcqlpUQmis09ZaSMv8L/Tw0TuwLAA==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.3", - "@storybook/builder-webpack4": "6.4.14", - "@storybook/core-client": "6.4.14", - "@storybook/core-common": "6.4.14", - "@storybook/core-events": "6.4.14", + "@storybook/builder-webpack4": "6.4.19", + "@storybook/core-client": "6.4.19", + "@storybook/core-common": "6.4.19", + "@storybook/core-events": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/csf-tools": "6.4.14", - "@storybook/manager-webpack4": "6.4.14", - "@storybook/node-logger": "6.4.14", + "@storybook/csf-tools": "6.4.19", + "@storybook/manager-webpack4": "6.4.19", + "@storybook/node-logger": "6.4.19", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.14", + "@storybook/store": "6.4.19", "@types/node": "^14.0.10", "@types/node-fetch": "^2.5.7", "@types/pretty-hrtime": "^1.0.0", @@ -2655,9 +28765,9 @@ } }, "@storybook/csf-tools": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-6.4.14.tgz", - "integrity": "sha512-mRFsIhzFA2JBeUqdvl6+WM6HmHXaWGLbCgalzGqX65i1pSvhmC3jHh0OTTypMj9XneWH6/cHQh7LvivYbjJ8Cg==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-6.4.19.tgz", + "integrity": "sha512-gf/zRhGoAVsFwSyV2tc+jeJfZQkxF6QsaZgbUSe24/IUvGFCT/PS/jZq1qy7dECAwrTOfykgu8juyBtj6WhWyw==", "dev": true, "requires": { "@babel/core": "^7.12.10", @@ -2680,20 +28790,20 @@ } }, "@storybook/manager-webpack4": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/manager-webpack4/-/manager-webpack4-6.4.14.tgz", - "integrity": "sha512-j565G7vZLBXK60J1hiZhbeZ6K48y8CMMZCcIihqsFv/4jj0kI3Ba4IhCrOkHiqiRM89mRu5/Ga3DnHTBvIYIEA==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/manager-webpack4/-/manager-webpack4-6.4.19.tgz", + "integrity": "sha512-R8ugZjTYqXvlc6gDOcw909L65sIleOmIJLZR+N6/H85MivGXHu39jOwONqB7tVACufRty4FNecn8tEiQL2SAKA==", "dev": true, "requires": { "@babel/core": "^7.12.10", "@babel/plugin-transform-template-literals": "^7.12.1", "@babel/preset-react": "^7.12.10", - "@storybook/addons": "6.4.14", - "@storybook/core-client": "6.4.14", - "@storybook/core-common": "6.4.14", - "@storybook/node-logger": "6.4.14", - "@storybook/theming": "6.4.14", - "@storybook/ui": "6.4.14", + "@storybook/addons": "6.4.19", + "@storybook/core-client": "6.4.19", + "@storybook/core-common": "6.4.19", + "@storybook/node-logger": "6.4.19", + "@storybook/theming": "6.4.19", + "@storybook/ui": "6.4.19", "@types/node": "^14.0.10", "@types/webpack": "^4.41.26", "babel-loader": "^8.0.0", @@ -2820,9 +28930,9 @@ } }, "@storybook/node-logger": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.4.14.tgz", - "integrity": "sha512-mowC0adx4hLtCqGMQKRfNmiRYAL2PYdk3ojc91qzIKNrjSYnE4U8d9qlw5WLx1PKEnZVji3+QiYfNHpA/8PoKw==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.4.19.tgz", + "integrity": "sha512-hO2Aar3PgPnPtNq2fVgiuGlqo3EEVR6TKVBXMq7foL3tN2k4BQFKLDHbm5qZQQntyYKurKsRUGKPJFPuI1ov/w==", "dev": true, "requires": { "@types/npmlog": "^4.1.2", @@ -2869,17 +28979,17 @@ } }, "@storybook/preview-web": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/preview-web/-/preview-web-6.4.14.tgz", - "integrity": "sha512-3E++OYz+OCyJBIchkNCJRtxEU7XNDBdIvKRTCx48X+Uv5qoLeCpXiXOSK/42LlraWZkfBs56yHv9VSqJoQ8VwA==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/preview-web/-/preview-web-6.4.19.tgz", + "integrity": "sha512-jqltoBv5j7lvnxEfV9w8dLX9ASWGuvgz97yg8Yo5FqkftEwrHJenyvMGcTgDJKJPorF+wiz/9aIqnmd3LCAcZQ==", "dev": true, "requires": { - "@storybook/addons": "6.4.14", - "@storybook/channel-postmessage": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/core-events": "6.4.14", + "@storybook/addons": "6.4.19", + "@storybook/channel-postmessage": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/store": "6.4.14", + "@storybook/store": "6.4.19", "ansi-to-html": "^0.6.11", "core-js": "^3.8.2", "global": "^4.4.0", @@ -2893,22 +29003,22 @@ } }, "@storybook/react": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-6.4.14.tgz", - "integrity": "sha512-wlPjE5Xcarc5NTgnHchvGE56EVYioAyRZoYvb/YyiCX1+A8sQkwS2qTTH8e/pdG539A4NMrciMosvjvEPZcEvg==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-6.4.19.tgz", + "integrity": "sha512-5b3i8jkVrjQGmcxxxXwCduHPIh+cluWkfeweKeQOe+lW4BR8fuUICo3AMLrYPAtB/UcaJyYkIYmTvF2mkfepFA==", "dev": true, "requires": { "@babel/preset-flow": "^7.12.1", "@babel/preset-react": "^7.12.10", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.1", - "@storybook/addons": "6.4.14", - "@storybook/core": "6.4.14", - "@storybook/core-common": "6.4.14", + "@storybook/addons": "6.4.19", + "@storybook/core": "6.4.19", + "@storybook/core-common": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/node-logger": "6.4.14", + "@storybook/node-logger": "6.4.19", "@storybook/react-docgen-typescript-plugin": "1.0.2-canary.253f8c1.0", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.14", + "@storybook/store": "6.4.19", "@types/webpack-env": "^1.16.0", "babel-plugin-add-react-displayname": "^0.0.5", "babel-plugin-named-asset-import": "^0.3.1", @@ -3005,12 +29115,12 @@ } }, "@storybook/router": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.4.14.tgz", - "integrity": "sha512-5+tePyINtwPYm4izgOBZ2sX2ViWtfmmO2vwOAPlWWEGzsRosVQsGMdZv1R8rk4Jl/TotMjlTmd8I1/BufEeIeQ==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.4.19.tgz", + "integrity": "sha512-KWWwIzuyeEIWVezkCihwY2A76Il9tUNg0I410g9qT7NrEsKyqXGRYOijWub7c1GGyNjLqz0jtrrehtixMcJkuA==", "dev": true, "requires": { - "@storybook/client-logger": "6.4.14", + "@storybook/client-logger": "6.4.19", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", @@ -3024,18 +29134,18 @@ }, "dependencies": { "react-router": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz", - "integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.2.tgz", + "integrity": "sha512-/MbxyLzd7Q7amp4gDOGaYvXwhEojkJD5BtExkuKmj39VEE0m3l/zipf6h2WIB2jyAO0lI6NGETh4RDcktRm4AQ==", "dev": true, "requires": { "history": "^5.2.0" }, "dependencies": { "history": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz", - "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", "dev": true, "requires": { "@babel/runtime": "^7.7.6" @@ -3044,19 +29154,19 @@ } }, "react-router-dom": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz", - "integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.2.tgz", + "integrity": "sha512-AtYEsAST7bDD4dLSQHDnk/qxWLJdad5t1HFa1qJyUrCeGgEuCSw0VB/27ARbF9Fi/W5598ujvJOm3ujUCVzuYQ==", "dev": true, "requires": { "history": "^5.2.0", - "react-router": "6.2.1" + "react-router": "6.2.2" }, "dependencies": { "history": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz", - "integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", "dev": true, "requires": { "@babel/runtime": "^7.7.6" @@ -3107,14 +29217,14 @@ } }, "@storybook/store": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/store/-/store-6.4.14.tgz", - "integrity": "sha512-D9KoJuNvwb9mEQD60GTPYSbQuXWZQHE8RBxCq7d7Qu46mrhlsNTOwt09lIgmuM3jAVto3FxnXY4U81RwJza7tg==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/store/-/store-6.4.19.tgz", + "integrity": "sha512-N9/ZjemRHGfT3InPIbqQqc6snkcfnf3Qh9oOr0smbfaVGJol//KOX65kzzobtzFcid0WxtTDZ3HmgFVH+GvuhQ==", "dev": true, "requires": { - "@storybook/addons": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/core-events": "6.4.14", + "@storybook/addons": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/core-events": "6.4.19", "@storybook/csf": "0.0.2--canary.87bc651.0", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", @@ -3138,15 +29248,15 @@ } }, "@storybook/theming": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.14.tgz", - "integrity": "sha512-kqmXNnIoOSAS4cgr9PitMgVrOps725O99eTsJNxB6J1Ide0CsA5v2tV6AmQn/scnpCQNr8uSjZerNlEcl/ensg==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.19.tgz", + "integrity": "sha512-V4pWmTvAxmbHR6B3jA4hPkaxZPyExHvCToy7b76DpUTpuHihijNDMAn85KhOQYIeL9q14zP/aiz899tOHsOidg==", "dev": true, "requires": { "@emotion/core": "^10.1.1", "@emotion/is-prop-valid": "^0.8.6", "@emotion/styled": "^10.0.27", - "@storybook/client-logger": "6.4.14", + "@storybook/client-logger": "6.4.19", "core-js": "^3.8.2", "deep-object-diff": "^1.1.0", "emotion-theming": "^10.0.27", @@ -3158,21 +29268,21 @@ } }, "@storybook/ui": { - "version": "6.4.14", - "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.14.tgz", - "integrity": "sha512-nZsd8GXzYwmmTjZUB7pJMh+Q1fST0d2lFkhDHakxLaPLwumibw9NHJ7bRWYHFlAVYpD0c2+POP3FpOW5Bjby1A==", + "version": "6.4.19", + "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.19.tgz", + "integrity": "sha512-gFwdn5LA2U6oQ4bfUFLyHZnNasGQ01YVdwjbi+l6yjmnckBNtZfJoVTZ1rzGUbxSE9rK48InJRU+latTsr7xAg==", "dev": true, "requires": { "@emotion/core": "^10.1.1", - "@storybook/addons": "6.4.14", - "@storybook/api": "6.4.14", - "@storybook/channels": "6.4.14", - "@storybook/client-logger": "6.4.14", - "@storybook/components": "6.4.14", - "@storybook/core-events": "6.4.14", - "@storybook/router": "6.4.14", + "@storybook/addons": "6.4.19", + "@storybook/api": "6.4.19", + "@storybook/channels": "6.4.19", + "@storybook/client-logger": "6.4.19", + "@storybook/components": "6.4.19", + "@storybook/core-events": "6.4.19", + "@storybook/router": "6.4.19", "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.4.14", + "@storybook/theming": "6.4.19", "copy-to-clipboard": "^3.3.1", "core-js": "^3.8.2", "core-js-pure": "^3.8.2", @@ -3266,9 +29376,9 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, "@types/json5": { @@ -3293,15 +29403,15 @@ "dev": true }, "@types/node": { - "version": "14.18.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.9.tgz", - "integrity": "sha512-j11XSuRuAlft6vLDEX4RvhqC0KxNxx6QIyMXNb0vHHSNPXTPeiy3algESWmOOIzEtiEL0qiowPU3ewW9hHVa7Q==", + "version": "14.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", + "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", "dev": true }, "@types/node-fetch": { - "version": "2.5.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz", - "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==", "dev": true, "requires": { "@types/node": "*", @@ -3363,9 +29473,9 @@ "dev": true }, "@types/react": { - "version": "17.0.38", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.38.tgz", - "integrity": "sha512-SI92X1IA+FMnP3qM5m4QReluXzhcmovhZnLNm3pyeQlooi02qI7sLiepEYqT678uNiyc25XfCqxREFpy3W7YhQ==", + "version": "17.0.43", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", + "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", "dev": true, "requires": { "@types/prop-types": "*", @@ -3374,9 +29484,9 @@ }, "dependencies": { "csstype": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", - "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", "dev": true } } @@ -3665,13 +29775,13 @@ "optional": true }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { @@ -4168,9 +30278,9 @@ "dev": true }, "aws-sdk": { - "version": "2.1063.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1063.0.tgz", - "integrity": "sha512-UonfKdsDChKEmAkFuDOQ8zeilvR5v7d5dEcWDy+fnKBs+6HGjDThMf7EofhOiKxOXWnFhrAsFKCsKDcfeA6NBg==", + "version": "2.1102.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1102.0.tgz", + "integrity": "sha512-MMOncE8IG3Dop3WPza6ryTAEz413ftn/MtDO7ouessb3ljlg5BfqRkTe/rhPH5svqEqJvlh7qHnK0VjgJwmLTQ==", "dev": true, "requires": { "buffer": "4.9.2", @@ -4185,9 +30295,9 @@ } }, "axe-core": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz", - "integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.1.tgz", + "integrity": "sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw==", "dev": true }, "axobject-query": { @@ -4197,13 +30307,13 @@ "dev": true }, "babel-loader": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.3.tgz", - "integrity": "sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz", + "integrity": "sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A==", "dev": true, "requires": { "find-cache-dir": "^3.3.1", - "loader-utils": "^1.4.0", + "loader-utils": "^2.0.0", "make-dir": "^3.1.0", "schema-utils": "^2.6.5" }, @@ -4229,26 +30339,6 @@ "path-exists": "^4.0.0" } }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -4427,13 +30517,13 @@ } }, "babel-plugin-polyfill-corejs3": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.1.tgz", - "integrity": "sha512-TihqEe4sQcb/QcPJvxe94/9RZuLQuF1+To4WqQcRvc+3J3gLCPIPgDKzGLG6zmQLfH3nn25heRuDNkS2KR4I8A==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", "dev": true, "requires": { "@babel/helper-define-polyfill-provider": "^0.3.1", - "core-js-compat": "^3.20.0" + "core-js-compat": "^3.21.0" } }, "babel-plugin-polyfill-regenerator": { @@ -4657,27 +30747,27 @@ "dev": true }, "body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", "dev": true, "requires": { - "bytes": "3.1.1", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", + "qs": "6.9.7", + "raw-body": "2.4.3", "type-is": "~1.6.18" }, "dependencies": { "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true }, "debug": { @@ -4696,9 +30786,9 @@ "dev": true }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", "dev": true } } @@ -4959,14 +31049,14 @@ } }, "browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.20.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz", + "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==", "requires": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", + "caniuse-lite": "^1.0.30001317", + "electron-to-chromium": "^1.4.84", "escalade": "^3.1.1", - "node-releases": "^2.0.1", + "node-releases": "^2.0.2", "picocolors": "^1.0.0" } }, @@ -5192,9 +31282,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001302", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001302.tgz", - "integrity": "sha512-YYTMO+tfwvgUN+1ZnRViE53Ma1S/oETg+J2lISsqi/ZTNThj3ZYBOKP2rHwJc37oCsPqAzJ3w2puZHn0xlLPPw==" + "version": "1.0.30001322", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz", + "integrity": "sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -5236,6 +31326,12 @@ "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", "dev": true }, + "charcodes": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/charcodes/-/charcodes-0.2.0.tgz", + "integrity": "sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==", + "dev": true + }, "check-types": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz", @@ -5464,9 +31560,9 @@ } }, "codemirror": { - "version": "5.65.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.1.tgz", - "integrity": "sha512-s6aac+DD+4O2u1aBmdxhB7yz2XU7tG3snOyQ05Kxifahz7hoxnfxIRHxiCSEv3TUC38dIVH8G+lZH9UWSfGQxA==" + "version": "5.65.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.2.tgz", + "integrity": "sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA==" }, "collapse-white-space": { "version": "1.0.6", @@ -5546,7 +31642,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true + "dev": true, + "optional": true }, "colorspace": { "version": "1.1.4", @@ -5755,9 +31852,9 @@ } }, "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "dev": true }, "cookie-signature": { @@ -5928,15 +32025,15 @@ } }, "core-js": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz", - "integrity": "sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==", + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", + "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", "dev": true }, "core-js-compat": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.3.tgz", - "integrity": "sha512-c8M5h0IkNZ+I92QhIpuSijOxGAcj3lgpsWdkCqmUTZNwidujF4r3pi6x1DCN+Vcs5qTS2XWWMfWSuCqyupX8gw==", + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz", + "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==", "dev": true, "requires": { "browserslist": "^4.19.1", @@ -5952,9 +32049,9 @@ } }, "core-js-pure": { - "version": "3.20.3", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.20.3.tgz", - "integrity": "sha512-Q2H6tQ5MtPtcC7f3HxJ48i4Q7T9ybPKgvWyuH7JXIoNa2pm0KuBnycsET/qw1SLLZYfbsbrZQNMeIOClb+6WIA==", + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz", + "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==", "dev": true }, "core-util-is": { @@ -6342,13 +32439,10 @@ } }, "css-declaration-sorter": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz", - "integrity": "sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw==", - "dev": true, - "requires": { - "timsort": "^0.3.0" - } + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz", + "integrity": "sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==", + "dev": true }, "css-loader": { "version": "3.6.0", @@ -6608,57 +32702,57 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, "cssnano": { - "version": "5.0.16", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.0.16.tgz", - "integrity": "sha512-ryhRI9/B9VFCwPbb1z60LLK5/ldoExi7nwdnJzpkLZkm2/r7j2X3jfY+ZvDVJhC/0fPZlrAguYdHNFg0iglPKQ==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.5.tgz", + "integrity": "sha512-VZO1e+bRRVixMeia1zKagrv0lLN1B/r/u12STGNNUFxnp97LIFgZHQa0JxqlwEkvzUyA9Oz/WnCTAFkdEbONmg==", "dev": true, "requires": { - "cssnano-preset-default": "^5.1.11", + "cssnano-preset-default": "^5.2.5", "lilconfig": "^2.0.3", "yaml": "^1.10.2" } }, "cssnano-preset-default": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.1.11.tgz", - "integrity": "sha512-ETet5hqHxmzQq2ynXMOQofKuLm7VOjMiOB7E2zdtm/hSeCKlD9fabzIUV4GoPcRyJRHi+4kGf0vsfGYbQ4nmPw==", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.5.tgz", + "integrity": "sha512-WopL7PzN7sos3X8B54/QGl+CZUh1f0qN4ds+y2d5EPwRSSc3jsitVw81O+Uyop0pXyOfPfZxnc+LmA8w/Ki/WQ==", "dev": true, "requires": { "css-declaration-sorter": "^6.0.3", - "cssnano-utils": "^3.0.1", - "postcss-calc": "^8.2.0", - "postcss-colormin": "^5.2.4", - "postcss-convert-values": "^5.0.3", - "postcss-discard-comments": "^5.0.2", - "postcss-discard-duplicates": "^5.0.2", - "postcss-discard-empty": "^5.0.2", - "postcss-discard-overridden": "^5.0.3", - "postcss-merge-longhand": "^5.0.5", - "postcss-merge-rules": "^5.0.5", - "postcss-minify-font-values": "^5.0.3", - "postcss-minify-gradients": "^5.0.5", - "postcss-minify-params": "^5.0.4", - "postcss-minify-selectors": "^5.1.2", - "postcss-normalize-charset": "^5.0.2", - "postcss-normalize-display-values": "^5.0.2", - "postcss-normalize-positions": "^5.0.3", - "postcss-normalize-repeat-style": "^5.0.3", - "postcss-normalize-string": "^5.0.3", - "postcss-normalize-timing-functions": "^5.0.2", - "postcss-normalize-unicode": "^5.0.3", - "postcss-normalize-url": "^5.0.4", - "postcss-normalize-whitespace": "^5.0.3", - "postcss-ordered-values": "^5.0.4", - "postcss-reduce-initial": "^5.0.2", - "postcss-reduce-transforms": "^5.0.3", - "postcss-svgo": "^5.0.3", - "postcss-unique-selectors": "^5.0.3" + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.0", + "postcss-discard-comments": "^5.1.1", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.3", + "postcss-merge-rules": "^5.1.1", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.2", + "postcss-minify-selectors": "^5.2.0", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.0", + "postcss-normalize-repeat-style": "^5.1.0", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.1", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" } }, "cssnano-utils": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.0.1.tgz", - "integrity": "sha512-VNCHL364lh++/ono+S3j9NlUK+d97KNkxI77NlqZU2W3xd2/qmyN61dsa47pTpb55zuU4G4lI7qFjAXZJH1OAQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", "dev": true }, "csso": { @@ -6695,9 +32789,9 @@ } }, "csstype": { - "version": "2.6.19", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.19.tgz", - "integrity": "sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ==", + "version": "2.6.20", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", + "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==", "dev": true }, "currency-symbol-map": { @@ -6852,9 +32946,9 @@ } }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -7139,33 +33233,14 @@ "path-type": "^4.0.0" } }, - "disposables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/disposables/-/disposables-1.0.2.tgz", - "integrity": "sha1-NsamdEdfVaLWkTVnpgFETkh7S24=" - }, "dnd-core": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", - "integrity": "sha1-ErrWbVh0LG5ffPKUP7aFlED4CcQ=", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-15.1.1.tgz", + "integrity": "sha512-Mtj/Sltcx7stVXzeDg4g7roTe/AmzRuIf/FYOxX6F8gULbY54w066BlErBOzQfn9RIJ3gAYLGX7wvVvoBSq7ig==", "requires": { - "asap": "^2.0.6", - "invariant": "^2.0.0", - "lodash": "^4.2.0", - "redux": "^3.7.1" - }, - "dependencies": { - "redux": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", - "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", - "requires": { - "lodash": "^4.2.1", - "lodash-es": "^4.2.1", - "loose-envify": "^1.1.0", - "symbol-observable": "^1.0.3" - } - } + "@react-dnd/asap": "4.0.0", + "@react-dnd/invariant": "3.0.0", + "redux": "^4.1.1" } }, "dns-equal": { @@ -7226,9 +33301,9 @@ }, "dependencies": { "csstype": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", - "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==" } } }, @@ -7441,9 +33516,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.53", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.53.tgz", - "integrity": "sha512-rFveSKQczlcav+H3zkKqykU6ANseFwXwkl855jOIap5/0gnEcuIhv2ecz6aoTrXavF6I/CEBeRnBnkB51k06ew==" + "version": "1.4.97", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.97.tgz", + "integrity": "sha512-vqSu7Qn6o5E1uAJQxmq2U69aBhBTxUAXMuT5Sm3jj8kEJciuUcKciktLuTPFSRlwSdNyeu9qah8Nzy9JyxefCw==" }, "element-resize-detector": { "version": "1.2.4", @@ -7607,18 +33682,18 @@ } }, "error-stack-parser": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz", - "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.7.tgz", + "integrity": "sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA==", "dev": true, "requires": { "stackframe": "^1.1.1" } }, "es-abstract": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", - "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz", + "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -7627,15 +33702,15 @@ "get-intrinsic": "^1.1.1", "get-symbol-description": "^1.0.0", "has": "^1.0.3", - "has-symbols": "^1.0.2", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.1", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.1", "is-string": "^1.0.7", - "is-weakref": "^1.0.1", - "object-inspect": "^1.11.0", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", "object-keys": "^1.1.1", "object.assign": "^4.1.2", "string.prototype.trimend": "^1.0.4", @@ -7685,19 +33760,19 @@ } }, "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "version": "0.10.59", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.59.tgz", + "integrity": "sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw==", "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" } }, "es5-shim": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.4.tgz", - "integrity": "sha512-Z0f7OUYZ8JfqT12d3Tgh2ErxIH5Shaz97GE8qyDG9quxb2Hmh2vvFHlOFjx6lzyD0CRgvJfnNYcisjdbRp7MPw==", + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.6.5.tgz", + "integrity": "sha512-vfQ4UAai8szn0sAubCy97xnZ4sJVDD1gt/Grn736hg8D7540wemIb1YPrYZSTqlM2H69EQX1or4HU/tSwRTI3w==", "dev": true }, "es6-error": { @@ -7827,9 +33902,9 @@ } }, "eslint-module-utils": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.2.tgz", - "integrity": "sha512-zquepFnWCY2ISMFwD/DqzaM++H+7PDzOpUvotJWm/y1BAFt5R4oeULgdrTejKqLkz7MA/tgstsUMNYc7wNdTrg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", + "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", "dev": true, "requires": { "debug": "^3.2.7", @@ -7955,9 +34030,9 @@ } }, "eslint-plugin-react": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz", - "integrity": "sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", + "integrity": "sha512-CVCXajliVh509PcZYRFyu/BoUEz452+jtQJq2b3Bae4v3xBUWPLCmtmBM+ZinG4MzwmxJgJ2M5rMqhqLVn7MtQ==", "dev": true, "requires": { "array-includes": "^3.1.4", @@ -7965,12 +34040,12 @@ "doctrine": "^2.1.0", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "object.entries": "^1.1.5", "object.fromentries": "^2.0.5", "object.hasown": "^1.1.0", "object.values": "^1.1.5", - "prop-types": "^15.7.2", + "prop-types": "^15.8.1", "resolve": "^2.0.0-next.3", "semver": "^6.3.0", "string.prototype.matchall": "^4.0.6" @@ -8289,17 +34364,17 @@ } }, "express": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", - "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", "dev": true, "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.1", + "body-parser": "1.19.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.1", + "cookie": "0.4.2", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "~1.1.2", @@ -8314,7 +34389,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.9.6", + "qs": "6.9.7", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.17.2", @@ -8342,9 +34417,9 @@ "dev": true }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", "dev": true }, "safe-buffer": { @@ -8364,9 +34439,9 @@ }, "dependencies": { "type": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", - "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", + "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" } } }, @@ -8455,8 +34530,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { "version": "3.2.11", @@ -8539,11 +34613,11 @@ } }, "fbjs": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.2.tgz", - "integrity": "sha512-qv+boqYndjElAJHNN3NoM8XuwQZ1j2m3kEvTgdle8IDjr6oUbkEpvABWtj/rQl3vq4ew7dnElBxL4YJAwTVqQQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", + "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", "requires": { - "cross-fetch": "^3.0.4", + "cross-fetch": "^3.1.5", "fbjs-css-vars": "^1.0.0", "loose-envify": "^1.0.0", "object-assign": "^4.1.0", @@ -8890,9 +34964,9 @@ } }, "flatted": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", - "integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, "flow-bin": { @@ -8953,9 +35027,9 @@ "dev": true }, "follow-redirects": { - "version": "1.14.7", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz", - "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==", + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", "dev": true }, "for-in": { @@ -9532,9 +35606,9 @@ } }, "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-tostringtag": { "version": "1.0.0", @@ -9842,9 +35916,9 @@ } }, "html-entities": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", - "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", "dev": true }, "html-escaper": { @@ -10065,9 +36139,9 @@ } }, "http-parser-js": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz", - "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", "dev": true }, "http-proxy": { @@ -10893,9 +36967,9 @@ } }, "istanbul-reports": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz", - "integrity": "sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.4.tgz", + "integrity": "sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==", "dev": true, "requires": { "html-escaper": "^2.0.0", @@ -10974,9 +37048,9 @@ } }, "jsbi": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.1.0.tgz", - "integrity": "sha512-384Z4keIsJtYpnVggsxaB255MZctILbxv+ihtwoWPF7KNOlYHn1LFpRnUw5qsAspUAA2+I7qzjVJxVYtHVjxNw==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.2.0.tgz", + "integrity": "sha512-JzhwPkH7Ra8O1b5uHmWxl6N8ZumqGvMXgpKcsq0/iWlnB+KkzbekCIcLl3hu3sFGTLNXAuXIqY4STtE0ZfT1zA==" }, "jsesc": { "version": "2.5.2", @@ -10996,6 +37070,16 @@ "lodash": "~4.17.21", "minimatch": "~3.0.2", "strip-json-comments": "1.0.x" + }, + "dependencies": { + "minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "json-parse-better-errors": { @@ -11022,20 +37106,11 @@ "integrity": "sha1-GjhU4o0rvuqzHMfd9oPS3cVlJwg=", "dev": true }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", - "dev": true - }, "json5": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", - "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true }, "jsonfile": { "version": "6.1.0", @@ -11153,9 +37228,9 @@ } }, "lilconfig": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", - "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", "dev": true }, "lines-and-columns": { @@ -11195,11 +37270,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, "lodash.curry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", @@ -11239,15 +37309,15 @@ "dev": true }, "logform": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.2.tgz", - "integrity": "sha512-V6JiPThZzTsbVRspNO6TmHkR99oqYTs8fivMBYQkjZj6rxW92KxtDCPE6IkAk1DNBnYKNkjm4jYBm6JDUcyhOA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz", + "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==", "dev": true, "requires": { - "colors": "1.4.0", + "@colors/colors": "1.5.0", "fecha": "^4.2.0", "ms": "^2.1.1", - "safe-stable-stringify": "^1.1.0", + "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, @@ -11349,15 +37419,15 @@ "dev": true }, "markdown-to-jsx": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.6.tgz", - "integrity": "sha512-1wrIGZYwIG2gR3yfRmbr4FlQmhaAKoKTpRo4wur4fp9p0njU1Hi7vR8fj0AUKKIcPduiJmPprzmCB5B/GvlC7g==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.1.7.tgz", + "integrity": "sha512-VI3TyyHlGkO8uFle0IOibzpO1c1iJDcXcS/zBrQrXQQvJ2tpdwVzVZ7XdKsyRz1NdRmre4dqQkMZzUHaKIG/1w==", "dev": true }, "math-expression-evaluator": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.3.11.tgz", - "integrity": "sha512-xOm6QnxKj4aS216gb2YMFbEl7PP4+BkvBjy0cphVq2FTaU6O3BWVPqMA4bsTeCTaMWDeLrC2fymbRQ98GACkeg==" + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.3.14.tgz", + "integrity": "sha512-M6AMrvq9bO8uL42KvQHPA2/SbAobA0R7gviUmPrcTcGfdwpaLitz4q2Euzx2lP9Oy88vxK3HOrsISgSwKsYS4A==" }, "md5-file": { "version": "5.0.0", @@ -11528,13 +37598,13 @@ "dev": true }, "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, "miller-rabin": { @@ -11562,18 +37632,18 @@ "dev": true }, "mime-db": { - "version": "1.51.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", - "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true }, "mime-types": { - "version": "2.1.34", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", - "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "requires": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" } }, "min-document": { @@ -11649,17 +37719,17 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "minio": { "version": "7.0.26", @@ -11812,23 +37882,23 @@ } }, "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "requires": { - "minimist": "^1.2.5" + "minimist": "^1.2.6" } }, "mobx": { - "version": "6.3.13", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.3.13.tgz", - "integrity": "sha512-zDDKDhYUk9QCHQUdLG+wb4Jv/nXutSLt/P8kkwHyjdbrJO4OZS6QTEsrOnrKM39puqXSrJZHdB6+yRys2NBFFA==" + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.5.0.tgz", + "integrity": "sha512-pHZ/cySF00FVENDWIDzJyoObFahK6Eg4d0papqm6d7yMkxWTZ/S/csqJX1A3PsYy4t5k3z2QnlwuCfMW5lSEwA==" }, "mobx-react-lite": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.2.3.tgz", - "integrity": "sha512-7exWp1FV0M9dP08H9PIeHlJqDw4IdkQVRMfLYaZFMmlbzSS6ZU6p/kx392KN+rVf81hH3IQYewvRGQ70oiwmbw==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.3.0.tgz", + "integrity": "sha512-U/kMSFtV/bNVgY01FuiGWpRkaQVHozBq5CEBZltFvPt4FcV111hEWkgwqVg9GPPZSEuEdV438PEz8mk8mKpYlA==" }, "moment": { "version": "2.29.1", @@ -11914,9 +37984,9 @@ } }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, "neo-async": { @@ -11926,15 +37996,15 @@ "dev": true }, "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", + "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", "dev": true }, "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" }, "nice-try": { "version": "1.0.5", @@ -12110,9 +38180,9 @@ } }, "node-releases": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" }, "normalize-package-data": { "version": "2.5.0", @@ -13083,13 +39153,13 @@ } }, "postcss-calc": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.2.tgz", - "integrity": "sha512-B5R0UeB4zLJvxNt1FVCaDZULdzsKLPc6FhjFJ+xwFiq7VG4i9cuaJLxVjNtExNK8ocm3n2o4unXXLiVX1SCqxA==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", "dev": true, "requires": { - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" }, "dependencies": { "postcss-value-parser": { @@ -13101,9 +39171,9 @@ } }, "postcss-colormin": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.2.4.tgz", - "integrity": "sha512-rYlC5015aNqVQt/B6Cy156g7sH5tRUJGmT9xeagYthtKehetbKx7jHxhyLpulP4bs4vbp8u/B2rac0J7S7qPQg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -13121,9 +39191,9 @@ } }, "postcss-convert-values": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.0.3.tgz", - "integrity": "sha512-fVkjHm2T0PSMqXUCIhHNWVGjhB9mHEWX2GboVs7j3iCgr6FpIl9c/IdXy0PHWZSQ9LFTRgmj98amxJE6KOnlsA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.0.tgz", + "integrity": "sha512-GkyPbZEYJiWtQB0KZ0X6qusqFHUepguBCNFi9t5JJc7I2OTXG7C0twbTLvCfaKOLl3rSXmpAwV7W5txd91V84g==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -13138,27 +39208,27 @@ } }, "postcss-discard-comments": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.2.tgz", - "integrity": "sha512-6VQ3pYTsJHEsN2Bic88Aa7J/Brn4Bv8j/rqaFQZkH+pcVkKYwxCIvoMQkykEW7fBjmofdTnQgcivt5CCBJhtrg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.1.tgz", + "integrity": "sha512-5JscyFmvkUxz/5/+TB3QTTT9Gi9jHkcn8dcmmuN68JQcv3aQg4y88yEHHhwFB52l/NkaJ43O0dbksGMAo49nfQ==", "dev": true }, "postcss-discard-duplicates": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.2.tgz", - "integrity": "sha512-LKY81YjUjc78p6rbXIsnppsaFo8XzCoMZkXVILJU//sK0DgPkPSpuq/cZvHss3EtdKvWNYgWzQL+wiJFtEET4g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", "dev": true }, "postcss-discard-empty": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.2.tgz", - "integrity": "sha512-SxBsbTjlsKUvZLL+dMrdWauuNZU8TBq5IOL/DHa6jBUSXFEwmDqeXRfTIK/FQpPTa8MJMxEHjSV3UbiuyLARPQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", "dev": true }, "postcss-discard-overridden": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.3.tgz", - "integrity": "sha512-yRTXknIZA4k8Yo4FiF1xbsLj/VBxfXEWxJNIrtIy6HC9KQ4xJxcPtoaaskh6QptCGrrcGnhKsTsENTRPZOBu4g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", "dev": true }, "postcss-flexbugs-fixes": { @@ -13403,13 +39473,13 @@ } }, "postcss-merge-longhand": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.0.5.tgz", - "integrity": "sha512-R2BCPJJ/U2oh1uTWEYn9CcJ7MMcQ1iIbj9wfr2s/zHu5om5MP/ewKdaunpfJqR1WYzqCsgnXuRoVXPAzxdqy8g==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.3.tgz", + "integrity": "sha512-lX8GPGvZ0iGP/IboM7HXH5JwkXvXod1Rr8H8ixwiA372hArk0zP4ZcCy4z4Prg/bfNlbbTf0KCOjCF9kKnpP/w==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.0.2" + "stylehacks": "^5.1.0" }, "dependencies": { "postcss-value-parser": { @@ -13421,21 +39491,21 @@ } }, "postcss-merge-rules": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.0.5.tgz", - "integrity": "sha512-3Oa26/Pb9VOFVksJjFG45SNoe4nhGvJ2Uc6TlRimqF8uhfOCEhVCaJ3rvEat5UFOn2UZqTY5Da8dFgCh3Iq0Ug==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.1.tgz", + "integrity": "sha512-8wv8q2cXjEuCcgpIB1Xx1pIy8/rhMPIQqYKNzEdyx37m6gpq83mQQdCxgIkFgliyEnKvdwJf/C61vN4tQDq4Ww==", "dev": true, "requires": { "browserslist": "^4.16.6", "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.1.0", "postcss-selector-parser": "^6.0.5" } }, "postcss-minify-font-values": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.0.3.tgz", - "integrity": "sha512-bC45rVzEwsLhv/cL1eCjoo2OOjbSk9I7HKFBYnBvtyuIZlf7uMipMATXtA0Fc3jwPo3wuPIW1jRJWKzflMh1sA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -13450,13 +39520,13 @@ } }, "postcss-minify-gradients": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.0.5.tgz", - "integrity": "sha512-/YjvXs8PepsoiZAIpjstOO4IHKwFAqYNqbA1yVdqklM84tbUUneh6omJxGlRlF3mi6K5Pa067Mg6IwqEnYC8Zg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", "dev": true, "requires": { "colord": "^2.9.1", - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "dependencies": { @@ -13469,13 +39539,13 @@ } }, "postcss-minify-params": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.0.4.tgz", - "integrity": "sha512-Z0vjod9lRZEmEPfEmA2sCfjbfEEFKefMD3RDIQSUfXK4LpCyWkX1CniUgyNvnjJFLDPSxtgKzozhHhPHKoeGkg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.2.tgz", + "integrity": "sha512-aEP+p71S/urY48HWaRHasyx4WHQJyOYaKpQ6eXl8k0kxg66Wt/30VR6/woh8THgcpRbonJD5IeD+CzNhPi1L8g==", "dev": true, "requires": { "browserslist": "^4.16.6", - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "dependencies": { @@ -13488,9 +39558,9 @@ } }, "postcss-minify-selectors": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.1.2.tgz", - "integrity": "sha512-gpn1nJDMCf3g32y/7kl+jsdamhiYT+/zmEt57RoT9GmzlixBNRPohI7k8UIHelLABhdLf3MSZhtM33xuH5eQOQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.0.tgz", + "integrity": "sha512-vYxvHkW+iULstA+ctVNx0VoRAR4THQQRkG77o0oa4/mBS0OzGvvzLIvHDv/nNEM0crzN2WIyFU5X7wZhaUK3RA==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.5" @@ -13830,15 +39900,15 @@ } }, "postcss-normalize-charset": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.2.tgz", - "integrity": "sha512-fEMhYXzO8My+gC009qDc/3bgnFP8Fv1Ic8uw4ec4YTlhIOw63tGPk1YFd7fk9bZUf1DAbkhiL/QPWs9JLqdF2g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", "dev": true }, "postcss-normalize-display-values": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.2.tgz", - "integrity": "sha512-RxXoJPUR0shSjkMMzgEZDjGPrgXUVYyWA/YwQRicb48H15OClPuaDR7tYokLAlGZ2tCSENEN5WxjgxSD5m4cUw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -13853,9 +39923,9 @@ } }, "postcss-normalize-positions": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.0.3.tgz", - "integrity": "sha512-U+rmhjrNBvIGYqr/1tD4wXPFFMKUbXsYXvlUCzLi0tOCUS6LoeEAnmVXXJY/MEB/1CKZZwBSs2tmzGawcygVBA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz", + "integrity": "sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -13870,9 +39940,9 @@ } }, "postcss-normalize-repeat-style": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.3.tgz", - "integrity": "sha512-uk1+xYx0AMbA3nLSNhbDrqbf/rx+Iuq5tVad2VNyaxxJzx79oGieJ6D9F6AfOL2GtiIbP7vTYlpYHtG+ERFXTg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz", + "integrity": "sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -13887,9 +39957,9 @@ } }, "postcss-normalize-string": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.0.3.tgz", - "integrity": "sha512-Mf2V4JbIDboNGQhW6xW0YREDiYXoX3WrD3EjKkjvnpAJ6W4qqjLnK/c9aioyVFaWWHVdP5zVRw/9DI5S3oLDFw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -13904,9 +39974,9 @@ } }, "postcss-normalize-timing-functions": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.2.tgz", - "integrity": "sha512-Ao0PP6MoYsRU1LxeVUW740ioknvdIUmfr6uAA3xWlQJ9s69/Tupy8qwhuKG3xWfl+KvLMAP9p2WXF9cwuk/7Bg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -13921,9 +39991,9 @@ } }, "postcss-normalize-unicode": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.3.tgz", - "integrity": "sha512-uNC7BmS/7h6to2UWa4RFH8sOTzu2O9dVWPE/F9Vm9GdhONiD/c1kNaCLbmsFHlKWcEx7alNUChQ+jH/QAlqsQw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -13939,9 +40009,9 @@ } }, "postcss-normalize-url": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.0.4.tgz", - "integrity": "sha512-cNj3RzK2pgQQyNp7dzq0dqpUpQ/wYtdDZM3DepPmFjCmYIfceuD9VIAcOdvrNetjIU65g1B4uwdP/Krf6AFdXg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", "dev": true, "requires": { "normalize-url": "^6.0.1", @@ -13963,9 +40033,9 @@ } }, "postcss-normalize-whitespace": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.3.tgz", - "integrity": "sha512-333JWRnX655fSoUbufJ10HJop3c8mrpKkCCUnEmgz/Cb/QEtW+/TMZwDAUt4lnwqP6tCCk0x0b58jqvDgiQm/A==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -13980,12 +40050,12 @@ } }, "postcss-ordered-values": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.0.4.tgz", - "integrity": "sha512-taKtGDZtyYUMVYkg+MuJeBUiTF6cGHZmo/qcW7ibvW79UlyKuSHbo6dpCIiqI+j9oJsXWzP+ovIxoyLDOeQFdw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz", + "integrity": "sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==", "dev": true, "requires": { - "cssnano-utils": "^3.0.1", + "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "dependencies": { @@ -13998,9 +40068,9 @@ } }, "postcss-reduce-initial": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz", - "integrity": "sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -14008,9 +40078,9 @@ } }, "postcss-reduce-transforms": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.3.tgz", - "integrity": "sha512-yDnTUab5i7auHiNwdcL1f+pBnqQFf+7eC4cbC7D8Lc1FkvNZhtpkdad+9U4wDdFb84haupMf0rA/Zc5LcTe/3A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -14062,12 +40132,12 @@ } }, "postcss-svgo": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.0.3.tgz", - "integrity": "sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", "dev": true, "requires": { - "postcss-value-parser": "^4.1.0", + "postcss-value-parser": "^4.2.0", "svgo": "^2.7.0" }, "dependencies": { @@ -14078,14 +40148,14 @@ "dev": true }, "css-select": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", - "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, "requires": { "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } @@ -14101,9 +40171,9 @@ } }, "css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.0.1.tgz", + "integrity": "sha512-z93ZGFLNc6yaoXAmVhqoSIb+BduplteCt1fepvwhBUQK6MNE4g6fgjpuZKJKp0esUe+vXWlIkwZZjNWoOKw0ZA==", "dev": true }, "dom-serializer": { @@ -14124,9 +40194,9 @@ "dev": true }, "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, "requires": { "domelementtype": "^2.2.0" @@ -14188,9 +40258,9 @@ } }, "postcss-unique-selectors": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.0.3.tgz", - "integrity": "sha512-V5tX2hadSSn+miVCluuK1IDGy+7jAXSOfRZ2DQ+s/4uQZb/orDYBjH0CHgFrXsRw78p4QTuEFA9kI6C956UnHQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.5" @@ -14234,9 +40304,9 @@ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" }, "prismjs": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz", - "integrity": "sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ==", + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", + "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", "dev": true }, "process": { @@ -14588,21 +40658,21 @@ "dev": true }, "raw-body": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", - "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", "dev": true, "requires": { - "bytes": "3.1.1", + "bytes": "3.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "dependencies": { "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true } } @@ -14780,31 +40850,23 @@ } }, "react-dnd": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", - "integrity": "sha1-f6JWds+CfViokSk+PBq1naACVFo=", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-15.1.1.tgz", + "integrity": "sha512-QLrHtPU08U4c5zop0ANeqrHXaQw2EWLMn8DQoN6/e4eSN/UbB84P49/80Qg0MEF29VLB5vikSoiFh9N8ASNmpQ==", "requires": { - "disposables": "^1.0.1", - "dnd-core": "^2.6.0", - "hoist-non-react-statics": "^2.1.0", - "invariant": "^2.1.0", - "lodash": "^4.2.0", - "prop-types": "^15.5.10" - }, - "dependencies": { - "hoist-non-react-statics": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" - } + "@react-dnd/invariant": "3.0.0", + "@react-dnd/shallowequal": "3.0.0", + "dnd-core": "15.1.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" } }, "react-dnd-html5-backend": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", - "integrity": "sha1-WQzRzKeEQbsnTt1XH+9MCxbdz44=", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-15.1.2.tgz", + "integrity": "sha512-mem9QbutUF+aA2YC1y47G3ECjnYV/sCYKSnu5Jd7cbg3fLMPAwbnTf/JayYdnCH5l3eg9akD9dQt+cD0UdF8QQ==", "requires": { - "lodash": "^4.2.0" + "dnd-core": "15.1.1" } }, "react-docgen": { @@ -14884,9 +40946,9 @@ } }, "react-helmet-async": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.2.2.tgz", - "integrity": "sha512-XgSQezeCbLfCxdZhDA3T/g27XZKnOYyOkruopTLSJj8RvFZwdXnM4djnfYaiBSDzOidDgTo1jcEozoRu/+P9UQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.2.3.tgz", + "integrity": "sha512-mCk2silF53Tq/YaYdkl2sB+/tDoPnaxN7dFS/6ZLJb/rhUY2EWGI5Xj2b4jHppScMqY45MbgPSwTxDchKpZ5Kw==", "dev": true, "requires": { "@babel/runtime": "^7.12.5", @@ -15384,22 +41446,14 @@ "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" }, "refractor": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.5.0.tgz", - "integrity": "sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", + "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", "dev": true, "requires": { "hastscript": "^6.0.0", "parse-entities": "^2.0.0", - "prismjs": "~1.25.0" - }, - "dependencies": { - "prismjs": { - "version": "1.25.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz", - "integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==", - "dev": true - } + "prismjs": "~1.27.0" } }, "regenerate": { @@ -15409,9 +41463,9 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", - "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", + "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", "dev": true, "requires": { "regenerate": "^1.4.2" @@ -15451,29 +41505,29 @@ } }, "regexpu-core": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", - "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.0.1.tgz", + "integrity": "sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==", "dev": true, "requires": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^9.0.0", - "regjsgen": "^0.5.2", - "regjsparser": "^0.7.0", + "regenerate-unicode-properties": "^10.0.1", + "regjsgen": "^0.6.0", + "regjsparser": "^0.8.2", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.0.0" } }, "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", + "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", "dev": true }, "regjsparser": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", - "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", + "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -15638,22 +41692,22 @@ "dev": true }, "css-select": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz", - "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, "requires": { "boolbase": "^1.0.0", - "css-what": "^5.1.0", - "domhandler": "^4.3.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "css-what": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", - "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.0.1.tgz", + "integrity": "sha512-z93ZGFLNc6yaoXAmVhqoSIb+BduplteCt1fepvwhBUQK6MNE4g6fgjpuZKJKp0esUe+vXWlIkwZZjNWoOKw0ZA==", "dev": true }, "dom-serializer": { @@ -15674,9 +41728,9 @@ "dev": true }, "domhandler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", - "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, "requires": { "domelementtype": "^2.2.0" @@ -15932,9 +41986,9 @@ } }, "safe-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz", - "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", "dev": true }, "safer-buffer": { @@ -16292,9 +42346,9 @@ } }, "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "simple-swizzle": { @@ -16512,9 +42566,9 @@ } }, "socket.io-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz", - "integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz", + "integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==", "requires": { "@socket.io/component-emitter": "~3.0.0", "debug": "~4.3.1" @@ -16540,17 +42594,16 @@ } }, "sockjs-client": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.2.tgz", - "integrity": "sha512-ZzRxPBISQE7RpzlH4tKJMQbHM9pabHluk0WBaxAQ+wm/UieeBVBou0p4wVnSQGN9QmpAZygQ0cDIypWuqOFmFQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz", + "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==", "dev": true, "requires": { - "debug": "^3.2.6", - "eventsource": "^1.0.7", - "faye-websocket": "^0.11.3", + "debug": "^3.2.7", + "eventsource": "^1.1.0", + "faye-websocket": "^0.11.4", "inherits": "^2.0.4", - "json3": "^3.3.3", - "url-parse": "^1.5.3" + "url-parse": "^1.5.10" }, "dependencies": { "debug": { @@ -16751,9 +42804,9 @@ "dev": true }, "stackframe": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz", - "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.1.tgz", + "integrity": "sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==", "dev": true }, "state-toggle": { @@ -16945,9 +42998,9 @@ "dev": true }, "store2": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/store2/-/store2-2.13.1.tgz", - "integrity": "sha512-iJtHSGmNgAUx0b/MCS6ASGxb//hGrHHRgzvN+K5bvkBTN7A9RTpPSf1WSp+nPGvWCJ1jRnvY7MKnuqfoi3OEqg==", + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/store2/-/store2-2.13.2.tgz", + "integrity": "sha512-CMtO2Uneg3SAz/d6fZ/6qbqqQHi2ynq6/KzMD/26gTkiEShCcpqFfTHgOxsE0egAq6SX3FmN4CeSqn8BzXQkJg==", "dev": true }, "stream-browserify": { @@ -17047,6 +43100,11 @@ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", "dev": true }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -17067,18 +43125,18 @@ } }, "string.prototype.matchall": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz", - "integrity": "sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", + "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", "es-abstract": "^1.19.1", "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.2", + "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", + "regexp.prototype.flags": "^1.4.1", "side-channel": "^1.0.4" } }, @@ -17124,11 +43182,6 @@ "define-properties": "^1.1.3" } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -17223,9 +43276,9 @@ } }, "stylehacks": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.0.2.tgz", - "integrity": "sha512-114zeJdOpTrbQYRD4OU5UWJ99LKUaqCPJTU1HQ/n3q3BwmllFN8kHENaLnOeqVq6AhXrWfxHNZTl33iJ4oy3cQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", "dev": true, "requires": { "browserslist": "^4.16.6", @@ -17311,11 +43364,6 @@ } } }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, "symbol.prototype.description": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/symbol.prototype.description/-/symbol.prototype.description-1.0.5.tgz", @@ -17528,16 +43576,23 @@ } }, "terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", + "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", "dev": true, "requires": { + "acorn": "^8.5.0", "commander": "^2.20.0", "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "dependencies": { + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -17813,12 +43868,6 @@ "setimmediate": "^1.0.4" } }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, "tiny-invariant": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", @@ -18039,14 +44088,14 @@ "dev": true }, "tsconfig-paths": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", - "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", "dev": true, "requires": { "@types/json5": "^0.0.29", "json5": "^1.0.1", - "minimist": "^1.2.0", + "minimist": "^1.2.6", "strip-bom": "^3.0.0" }, "dependencies": { @@ -18242,9 +44291,9 @@ "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==" }, "uglify-js": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.0.tgz", - "integrity": "sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg==", + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.3.tgz", + "integrity": "sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==", "dev": true, "optional": true }, @@ -18557,9 +44606,9 @@ } }, "url-parse": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz", - "integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==", + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, "requires": { "querystringify": "^2.1.1", @@ -19367,9 +45416,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "camelcase": { @@ -19776,9 +45825,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "strip-ansi": { @@ -20004,6 +46053,15 @@ "ajv-keywords": "^3.1.0" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -20016,9 +46074,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "strip-ansi": { @@ -20032,15 +46090,6 @@ } } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -20091,9 +46140,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true }, "strip-ansi": { @@ -20316,20 +46365,21 @@ } }, "winston": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.4.0.tgz", - "integrity": "sha512-FqilVj+5HKwCfIHQzMxrrd5tBIH10JTS3koFGbLVWBODjiIYq7zir08rFyBT4rrTYG/eaTqDcfSIbcjSM78YSw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz", + "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==", "dev": true, "requires": { "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", - "logform": "^2.3.2", + "logform": "^2.4.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.4.2" + "winston-transport": "^4.5.0" }, "dependencies": { "is-stream": { @@ -20367,14 +46417,14 @@ } }, "winston-transport": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.2.tgz", - "integrity": "sha512-9jmhltAr5ygt5usgUTQbEiw/7RYXpyUbEAFRCSicIacpUzPkrnQsQZSPGEI12aLK9Jth4zNcYJx3Cvznwrl8pw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", "dev": true, "requires": { "logform": "^2.3.2", - "readable-stream": "^3.4.0", - "triple-beam": "^1.2.0" + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" }, "dependencies": { "readable-stream": { @@ -20462,9 +46512,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "ws": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz", - "integrity": "sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", "dev": true }, "xml": { @@ -20518,9 +46568,9 @@ "dev": true }, "yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", + "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", "dev": true, "requires": { "cliui": "^7.0.2", @@ -20539,9 +46589,9 @@ "dev": true }, "yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", "dev": true } } diff --git a/frontend/package.json b/frontend/package.json index 8a3f3e4fc..5806baf98 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,8 +37,8 @@ "react-confirm": "^0.1.20", "react-datepicker": "^2.16.0", "react-daterange-picker": "^2.0.1", - "react-dnd": "^2.6.0", - "react-dnd-html5-backend": "^2.6.0", + "react-dnd": "^15.1.1", + "react-dnd-html5-backend": "^15.1.2", "react-dom": "^16.13.1", "react-draggable": "^4.4.4", "react-google-recaptcha": "^1.1.0", From 3ae300f73e1102e7313f1f872dc8e77c55ae7eaa Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 29 Mar 2022 15:51:52 +0200 Subject: [PATCH 22/87] feat(ui) - dashboard - wip --- frontend/app/Router.js | 7 +-- .../app/components/Dashboard/NewDashboard.tsx | 8 +-- .../Dashboard/WidgetWrapper/WidgetWrapper.tsx | 50 +++++++++---------- .../WidgetPreview/WidgetPreview.tsx | 2 +- .../app/components/ui/ItemMenu/ItemMenu.js | 6 ++- 5 files changed, 38 insertions(+), 35 deletions(-) diff --git a/frontend/app/Router.js b/frontend/app/Router.js index a2e36dd8f..e270afaba 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -50,7 +50,7 @@ const withObTab = routes.withObTab; const DASHBOARD_PATH = routes.dashboard(); const DASHBOARD_SELECT_PATH = routes.dashboardSelected(); -const DASHBOARD_METRICS_PATH = routes.dashboardMetrics(); +const DASHBOARD_METRICS_PATH = routes.dashboardMetricCreate(); // const WIDGET_PATAH = routes.dashboardMetric(); const SESSIONS_PATH = routes.sessions(); @@ -186,8 +186,9 @@ class Router extends React.Component { } - {/* */} - + + + {/* diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 5d51e5037..f4b6aec3e 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -56,6 +56,9 @@ function NewDashboard(props) {
+ + + { dashboardId && ( <> @@ -69,9 +72,8 @@ function NewDashboard(props) {
- {/* - - + + {/* */} diff --git a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx index d7f6e2fe4..86ce18b36 100644 --- a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx @@ -4,10 +4,16 @@ import cn from 'classnames'; import { ItemMenu } from 'UI'; import { useDrag, useDrop } from 'react-dnd'; -function WidgetWrapper(props) { - const { widget, index, moveListItem } = props; +interface Props { + className?: string; + widget?: any; + index?: number; + moveListItem?: any; + isPreview?: boolean; +} +function WidgetWrapper(props: Props) { + const { widget = {}, index = 0, moveListItem = null, isPreview = false } = props; - // useDrag - the list item is draggable const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', item: { index }, @@ -15,41 +21,33 @@ function WidgetWrapper(props) { isDragging: monitor.isDragging(), opacity: monitor.isDragging() ? 0.5 : 1, }), - }, [index]); - // useDrop - the list item is also a drop area - const [spec, dropRef] = useDrop({ + }); + + const [{ isOver, canDrop }, dropRef] = useDrop({ accept: 'item', + drop: (item: any) => { if (item.index === index) return; moveListItem(item.index, index); }, - // hover: (item: any, monitor: any) => { - // const dragIndex = item.index - // const hoverIndex = index - // const hoverBoundingRect = ref.current?.getBoundingClientRect() - // const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 - // const hoverActualY = monitor.getClientOffset().y - hoverBoundingRect.top - - // // if dragging down, continue only when hover is smaller than middle Y - // if (dragIndex < hoverIndex && hoverActualY < hoverMiddleY) return - // // if dragging up, continue only when hover is bigger than middle Y - // if (dragIndex > hoverIndex && hoverActualY > hoverMiddleY) return - - // moveListItem(dragIndex, hoverIndex) - // item.index = hoverIndex - // }, - }, []) - - console.log('spec', spec) + collect: (monitor: any) => ({ + isOver: monitor.isOver(), + canDrop: monitor.canDrop(), + }), + }) const ref: any = useRef(null) const dragDropRef: any = dragRef(dropRef(ref)) return (
{/* */} diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index 4e6de9662..12ec3113b 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -78,7 +78,7 @@ function WidgetPreview(props: Props) {
- +
)); diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.js b/frontend/app/components/ui/ItemMenu/ItemMenu.js index 94274a405..1d908c33e 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.js +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.js @@ -27,7 +27,9 @@ export default class ItemMenu extends React.PureComponent { } toggleMenu = (e) => { - e.stopPropagation(); + // e.preventDefault(); + // e.stopPropagation(); + console.log('toggleMenu', e); this.setState({ displayed: !this.state.displayed }); } @@ -51,7 +53,7 @@ export default class ItemMenu extends React.PureComponent { className="w-10 h-10 cursor-pointer bg-white rounded-full flex items-center justify-center hover:bg-gray-lightest" onClick={ this.toggleMenu } role="button" - tabIndex="-1" + // tabIndex="-1" >
From fe53ee07d7fbe9aec3140c4a9387f92cd6e43011 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 29 Mar 2022 18:33:41 +0200 Subject: [PATCH 23/87] feat(api): dashboards 3/5 --- api/chalicelib/core/dashboards2.py | 108 ++++++++++++++---- api/chalicelib/core/templates.py | 20 ---- api/routers/subs/metrics.py | 59 +++++++--- api/schemas.py | 5 +- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 1 + .../db/init_dbs/postgresql/init_schema.sql | 1 + 6 files changed, 129 insertions(+), 65 deletions(-) delete mode 100644 api/chalicelib/core/templates.py diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index c36293059..215a4f9ea 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -4,6 +4,24 @@ import schemas from chalicelib.utils import helper from chalicelib.utils import pg_client +CATEGORY_DESCRIPTION = { + 'categ1': 'lorem', +} + + +def get_templates(project_id, user_id): + with pg_client.PostgresClient() as cur: + pg_query = cur.mogrify(f"""SELECT category, jsonb_agg(metrics ORDER BY name) AS widgets + FROM metrics + WHERE deleted_at IS NULL AND (project_id ISNULL OR (project_id = %(project_id)s AND (is_public OR user_id= %(userId)s))) + GROUP BY category + ORDER BY category;""", {"project_id": project_id, "userId": user_id}) + cur.execute(pg_query) + rows = cur.fetchall() + for r in rows: + r["description"] = CATEGORY_DESCRIPTION.get(r["category"], "") + return helper.list_to_camel_case(rows) + def create_dashboard(project_id, user_id, data: schemas.CreateDashboardSchema): with pg_client.PostgresClient() as cur: @@ -31,15 +49,10 @@ def get_dashboards(project_id, user_id): def get_dashboard(project_id, user_id, dashboard_id): with pg_client.PostgresClient() as cur: - pg_query = """SELECT dashboards.*, all_template_widgets.widgets AS template_widgets, all_metric_widgets.widgets AS metric_widgets + pg_query = """SELECT dashboards.*, all_metric_widgets.widgets AS widgets FROM dashboards - LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG(templates), '[]'::jsonb) AS widgets - FROM templates - INNER JOIN dashboard_widgets USING (template_id) - WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id - ) AS all_template_widgets ON (TRUE) LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG(raw_metrics), '[]') AS widgets - FROM (SELECT metrics.*, metric_series.series + FROM (SELECT dashboard_widgets.*, metrics.*, metric_series.series FROM metrics INNER JOIN dashboard_widgets USING (metric_id) LEFT JOIN LATERAL (SELECT JSONB_AGG(metric_series.* ORDER BY index) AS series @@ -59,35 +72,84 @@ def get_dashboard(project_id, user_id, dashboard_id): print(cur.mogrify(pg_query, params)) cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() - row["widgets"] = row.pop("template_widgets") + row.pop("metric_widgets") + return helper.dict_to_camel_case(row) + + +def delete_dashboard(project_id, user_id, dashboard_id): + with pg_client.PostgresClient() as cur: + pg_query = """UPDATE dashboards + SET deleted_at = timezone('utc'::text, now()) + WHERE dashboards.project_id = %(projectId)s + AND dashboard_id = %(dashboard_id)s + AND (dashboards.user_id = %(userId)s OR is_public);""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id} + cur.execute(cur.mogrify(pg_query, params)) + return {"data": {"success": True}} + + +def update_dashboard(project_id, user_id, dashboard_id, data: schemas.CreateDashboardSchema): + with pg_client.PostgresClient() as cur: + pg_query = """UPDATE dashboards + SET name = %(name)s, is_pinned = %(is_pinned)s, is_public = %(is_public)s + WHERE dashboards.project_id = %(projectId)s + AND dashboard_id = %(dashboard_id)s + AND (dashboards.user_id = %(userId)s OR is_public) + RETURNING *;""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()} + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() return helper.dict_to_camel_case(row) def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashboardPayloadSchema): - ref_key = "metric_id" - if data.template_id is not None: - ref_key = "template_id" with pg_client.PostgresClient() as cur: - pg_query = f"""INSERT INTO dashboard_widgets(dashboard_id, {ref_key}, user_id, configuration, name) - VALUES (%(dashboard_id)s, %({ref_key})s, %(userId)s, %(configuration)s::jsonb, %(name)s) + pg_query = """INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config, name) + SELECT %(dashboard_id)s AS dashboard_id, %(metric_id)s AS metric_id, + %(userId)s AS user_id, %(config)s::jsonb AS config, %(name)s AS name + WHERE EXISTS(SELECT 1 FROM dashboards + WHERE dashboards.deleted_at ISNULL AND dashboards.project_id = %(projectId)s + AND dashboard_id = %(dashboard_id)s + AND (dashboards.user_id = %(userId)s OR is_public)) RETURNING *;""" params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()} - params["configuration"] = json.dumps(params.get("configuration", {})) + params["config"] = json.dumps(data.config) cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() return helper.dict_to_camel_case(row) -def remove_widget(project_id, user_id, dashboard_id,widget_id): - ref_key = "metric_id" - if data.template_id is not None: - ref_key = "template_id" + +def update_widget(project_id, user_id, dashboard_id, widget_id, data: schemas.AddWidgetToDashboardPayloadSchema): with pg_client.PostgresClient() as cur: - pg_query = f"""INSERT INTO dashboard_widgets(dashboard_id, {ref_key}, user_id, configuration, name) - VALUES (%(dashboard_id)s, %({ref_key})s, %(userId)s, %(configuration)s::jsonb, %(name)s) - RETURNING *;""" - params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()} - params["configuration"] = json.dumps(params.get("configuration", {})) + pg_query = """UPDATE dashboard_widgets + SET name= %(name)s, config= %(config)s + WHERE dashboard_id=%(dashboard_id)s AND widget_id=%(widget_id)s + RETURNINIG *;""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, + "widget_id": widget_id, **data.dict()} cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() return helper.dict_to_camel_case(row) + +def remove_widget(project_id, user_id, dashboard_id, widget_id): + with pg_client.PostgresClient() as cur: + pg_query = """DELETE FROM dashboard_widgets + WHERE dashboard_id=%(dashboard_id)s AND widget_id=%(widget_id)s;""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, "widget_id": widget_id} + cur.execute(cur.mogrify(pg_query, params)) + return {"data": {"success": True}} + + +def pin_dashboard(project_id, user_id, dashboard_id): + with pg_client.PostgresClient() as cur: + pg_query = """UPDATE dashboards + SET is_pinned = FALSE + WHERE dashboard_id=%(dashboard_id)s AND project_id=%(project_id)s; + UPDATE dashboards + SET is_pinned = True + WHERE dashboard_id=%(dashboard_id)s AND project_id=%(project_id)s AND deleted_at ISNULL + RETURNING *;""" + params = {"userId": user_id, "project_id": project_id, "dashboard_id": dashboard_id} + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return helper.dict_to_camel_case(row) diff --git a/api/chalicelib/core/templates.py b/api/chalicelib/core/templates.py deleted file mode 100644 index 8e42668d3..000000000 --- a/api/chalicelib/core/templates.py +++ /dev/null @@ -1,20 +0,0 @@ -from chalicelib.utils import helper -from chalicelib.utils import pg_client - -CATEGORY_DESCRIPTION = { - 'categ1': 'lorem', -} - - -def get_templates(project_id, user_id): - with pg_client.PostgresClient() as cur: - pg_query = cur.mogrify(f"""SELECT category, jsonb_agg(metrics ORDER BY name) AS widgets - FROM metrics - WHERE project_id ISNULL OR (project_id = %(project_id)s AND (is_public OR user_id= %(userId)s)) - GROUP BY category - ORDER BY category;""", {"project_id": project_id, "userId": user_id}) - cur.execute(pg_query) - rows = cur.fetchall() - for r in rows: - r["description"] = CATEGORY_DESCRIPTION.get(r["category"], "") - return helper.list_to_camel_case(rows) diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 56b486977..2d3c116d3 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -1,32 +1,50 @@ from fastapi import Body, Depends import schemas -from chalicelib.core import dashboards2, templates, custom_metrics +from chalicelib.core import dashboards2, custom_metrics from or_dependencies import OR_context from routers.base import get_routers public_app, app, app_apikey = get_routers() -@app.post('/{projectId}/dashboards', tags=["dashboard", "metrics"]) -@app.put('/{projectId}/dashboards', tags=["dashboard", "metrics"]) +@app.post('/{projectId}/dashboards', tags=["dashboard"]) +@app.put('/{projectId}/dashboards', tags=["dashboard"]) def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)} -@app.get('/{projectId}/dashboards', tags=["dashboard", "metrics"]) +@app.get('/{projectId}/dashboards', tags=["dashboard"]) def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.get_dashboards(project_id=projectId, user_id=context.user_id)} -@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"]) +@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} -@app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"]) -@app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"]) +@app.post('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) +@app.put('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) +def update_dashboard(projectId: int, dashboardId: int, data: schemas.CreateDashboardSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.update_dashboard(project_id=projectId, user_id=context.user_id, + dashboard_id=dashboardId, data=data)} + + +@app.delete('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) +def delete_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): + return dashboards2.delete_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId) + + +@app.get('/{projectId}/dashboards/{dashboardId}/pin', tags=["dashboard"]) +def pin_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.pin_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} + + +@app.post('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"]) +@app.put('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"]) def add_widget_to_dashboard(projectId: int, dashboardId: int, data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): @@ -34,22 +52,25 @@ def add_widget_to_dashboard(projectId: int, dashboardId: int, data=data)} -@app.delete('/{projectId}/dashboards/{dashboardId}/metrics/{metricId}', tags=["dashboard", "metrics"]) -def remove_widget_from_dashboard(projectId: int, dashboardId: int, metricId: int, - data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), +@app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) +@app.put('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) +def update_widget_in_dashboard(projectId: int, dashboardId: int, widgetId: int, + data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return dashboards2.update_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + widget_id=widgetId, data=data) + + +@app.delete('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) +def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, - data=data)} + return dashboards2.remove_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + widget_id=widgetId) -# @app.get('/{projectId}/widgets', tags=["dashboard", "metrics"]) -# def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): -# return {"data": dashboards2.get_widgets(project_id=projectId)} - - -@app.get('/{projectId}/metrics/templates', tags=["dashboard", "metrics"]) +@app.get('/{projectId}/metrics/templates', tags=["dashboard"]) def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": templates.get_templates(project_id=projectId, user_id=context.user_id)} + return {"data": dashboards2.get_templates(project_id=projectId, user_id=context.user_id)} @app.post('/{projectId}/metrics/try', tags=["dashboard"]) diff --git a/api/schemas.py b/api/schemas.py index bb111d61b..ad9558a4a 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -878,7 +878,7 @@ class SavedSearchSchema(FunnelSchema): class CreateDashboardSchema(BaseModel): - name: str = Field(...) + name: str = Field(..., min_length=1) is_public: bool = Field(default=False) is_pinned: bool = Field(default=False) @@ -887,10 +887,9 @@ class CreateDashboardSchema(BaseModel): class AddWidgetToDashboardPayloadSchema(BaseModel): - template_id: Optional[int] = Field(default=None) metric_id: Optional[int] = Field(default=None) name: Optional[str] = Field(default=None) - configuration: dict = Field(default={}) + config: dict = Field(default={}) @root_validator def validator(cls, values): diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index d0d31ecce..13e9e9e14 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -53,6 +53,7 @@ ALTER TABLE IF EXISTS metrics DROP CONSTRAINT IF EXISTS null_project_id_for_template_only; ALTER TABLE IF EXISTS metrics + ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 52f367e8e..35926bc96 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -953,6 +953,7 @@ $$ metric_value text[] NOT NULL DEFAULT '{}'::text[], metric_format text, category text NULL DEFAULT 'custom', + is_pinned boolean NOT NULL DEFAULT FALSE, is_predefined boolean NOT NULL DEFAULT FALSE, is_template boolean NOT NULL DEFAULT FALSE, key text NULL DEFAULT NULL, From b5932308be1b7edb0c77d0a767c2399154a4253a Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 29 Mar 2022 18:45:58 +0200 Subject: [PATCH 24/87] feat(api): build local script --- api/build_local.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 api/build_local.sh diff --git a/api/build_local.sh b/api/build_local.sh new file mode 100644 index 000000000..4253fd812 --- /dev/null +++ b/api/build_local.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -f ./Dockerfile --build-arg envarg="default-foss" -t chalice:local . \ No newline at end of file From 9f2db6f42e926ef1f29b57aa8c10a8d95f339d0c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 29 Mar 2022 19:58:22 +0200 Subject: [PATCH 25/87] feat(api): dashboard split old metrics grouped data 1/3 --- api/chalicelib/core/dashboard.py | 273 ++++++++++++++++++++++++++++--- api/routers/subs/dashboard.py | 27 +++ api/schemas.py | 8 + 3 files changed, 284 insertions(+), 24 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 9cd88eb6a..72995c27a 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -138,7 +138,7 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) with pg_client.PostgresClient() as cur: pg_query = f"""\ SELECT generated_timestamp AS timestamp, - COALESCE(COUNT(sessions), 0) AS count + COALESCE(COUNT(sessions), 0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT 1 FROM public.sessions @@ -151,7 +151,7 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) cur.execute(cur.mogrify(pg_query, params)) rows = cur.fetchall() results = { - "count": sum([r["count"] for r in rows]), + "value": sum([r["value"] for r in rows]), "chart": rows } @@ -170,7 +170,7 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) count = cur.fetchone()["count"] - results["countProgress"] = helper.__progress(old_val=count, new_val=results["count"]) + results["progress"] = helper.__progress(old_val=count, new_val=results["value"]) return results @@ -468,8 +468,9 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), ORDER BY generated_timestamp) AS chart ) AS chart ON (TRUE);""" - cur.execute(cur.mogrify(pg_query, {"step_size": step_size,"project_id": project_id, "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, **__get_constraint_values(args)})) + cur.execute( + cur.mogrify(pg_query, {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)})) rows = cur.fetchall() for i in range(len(rows)): rows[i]["sessions"] = rows[i].pop("sessions_count") @@ -672,8 +673,8 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, WHERE {" AND ".join(pg_sub_query)} AND positionUTF8(url_path, %(value)s) != 0 LIMIT 10);""" print(cur.mogrify(pg_query, {"project_id": project_id, - "value": helper.string_to_sql_like(text.lower()), - "platform_0": platform})) + "value": helper.string_to_sql_like(text.lower()), + "platform_0": platform})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text.lower()), "platform_0": platform})) @@ -691,9 +692,9 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, WHERE {" AND ".join(pg_sub_query)} LIMIT 10;""" print(cur.mogrify(pg_query, {"project_id": project_id, - "value": helper.string_to_sql_like(text), - "resource_type": resource_type, - "platform_0": platform})) + "value": helper.string_to_sql_like(text), + "resource_type": resource_type, + "platform_0": platform})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "resource_type": resource_type, @@ -709,8 +710,8 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, WHERE {" AND ".join(pg_sub_query)} LIMIT 10;""" print(cur.mogrify(pg_query, {"project_id": project_id, - "value": helper.string_to_sql_like(text), - "platform_0": platform})) + "value": helper.string_to_sql_like(text), + "platform_0": platform})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "platform_0": platform})) @@ -723,8 +724,8 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, WHERE {" AND ".join(pg_sub_query)} LIMIT 10;""" print(cur.mogrify(pg_query, {"project_id": project_id, - "value": helper.string_to_sql_like(text), - "platform_0": platform})) + "value": helper.string_to_sql_like(text), + "platform_0": platform})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "platform_0": platform})) @@ -737,8 +738,8 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, WHERE {" AND ".join(pg_sub_query)} LIMIT 10;""" print(cur.mogrify(pg_query, {"project_id": project_id, - "value": helper.string_to_sql_like(text), - "platform_0": platform})) + "value": helper.string_to_sql_like(text), + "platform_0": platform})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "platform_0": platform})) @@ -758,8 +759,8 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, WHERE {" AND ".join(pg_sub_query)} LIMIT 10;""" print(cur.mogrify(pg_query, - {"project_id": project_id, "value": helper.string_to_sql_like(text), "key": key, - "platform_0": platform})) + {"project_id": project_id, "value": helper.string_to_sql_like(text), "key": key, + "platform_0": platform})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "key": key, "platform_0": platform})) @@ -785,9 +786,9 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, LIMIT 10)""") pg_query = " UNION ALL ".join(pg_query) print(cur.mogrify(pg_query, - {"project_id": project_id, "value": helper.string_to_sql_like(text), - "key": key, - "platform_0": platform})) + {"project_id": project_id, "value": helper.string_to_sql_like(text), + "key": key, + "platform_0": platform})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "key": key, @@ -866,8 +867,6 @@ def get_network(project_id, startTimestamp=TimeUTC.now(delta_days=-1), pg_sub_query_subset.append("resources.timestamp>=%(startTimestamp)s") pg_sub_query_subset.append("resources.timestamp<%(endTimestamp)s") - - with pg_client.PostgresClient() as cur: pg_query = f"""WITH resources AS (SELECT resources.session_id, resources.url_hostpath, @@ -1952,7 +1951,7 @@ def get_errors_per_type(project_id, startTimestamp=TimeUTC.now(delta_days=-1), e pg_sub_query_subset.append("resources.status > 200") pg_sub_query_subset_e = __get_constraints(project_id=project_id, data=args, duration=False, main_table="m_errors", - time_constraint=False) + time_constraint=False) pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=False, chart=True, data=args, main_table="", time_column="timestamp", project=False, duration=False) @@ -2284,3 +2283,229 @@ def get_resources_by_party(project_id, startTimestamp=TimeUTC.now(delta_days=-1) rows = cur.fetchall() return rows + + +def __get_application_activity_avg_image_load_time(cur, project_id, startTimestamp, endTimestamp, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query.append("resources.duration > 0") + pg_sub_query.append("resources.type= %(type)s") + pg_query = f"""\ + SELECT COALESCE(AVG(resources.duration),0) AS value + FROM events.resources INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)};""" + + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "type": 'img', "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)})) + row = cur.fetchone() + return row + + +def get_application_activity_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with pg_client.PostgresClient() as cur: + row = __get_application_activity_avg_image_load_time(cur, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + row = __get_application_activity_avg_image_load_time(cur, project_id, startTimestamp, endTimestamp, **args) + previous = helper.dict_to_camel_case(row) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["chart"] = get_performance_avg_image_load_time(project_id, startTimestamp, endTimestamp, **args) + return results + + +def get_performance_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), + density=19, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) + img_constraints = [] + + img_constraints_vals = {} + + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + with pg_client.PostgresClient() as cur: + pg_sub_query_subset = __get_constraints(project_id=project_id, time_constraint=True, + chart=False, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=False, project=False, + chart=True, data=args, main_table="resources", time_column="timestamp", + duration=False) + pg_sub_query_subset.append("resources.timestamp >= %(startTimestamp)s") + pg_sub_query_subset.append("resources.timestamp < %(endTimestamp)s") + + pg_query = f"""WITH resources AS (SELECT resources.duration, resources.timestamp + FROM events.resources INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_subset)} + AND resources.type = 'img' AND resources.duration>0 + {(f' AND ({" OR ".join(img_constraints)})') if len(img_constraints) > 0 else ""} + ) + SELECT + generated_timestamp AS timestamp, + COALESCE(AVG(resources.duration),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT resources.duration + FROM resources + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS resources ON (TRUE) + GROUP BY timestamp + ORDER BY timestamp;""" + cur.execute(cur.mogrify(pg_query, {**params, **img_constraints_vals, **__get_constraint_values(args)})) + rows = cur.fetchall() + rows = helper.list_to_camel_case(rows) + + return rows + + +def __get_application_activity_avg_page_load_time(cur, project_id, startTimestamp, endTimestamp, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query.append("pages.timestamp >= %(startTimestamp)s") + pg_sub_query.append("pages.timestamp > %(endTimestamp)s") + pg_sub_query.append("pages.load_time > 0") + pg_sub_query.append("pages.load_time IS NOT NULL") + pg_query = f"""\ + SELECT COALESCE(AVG(pages.load_time) ,0) AS value + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)};""" + params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return row + + +@dev.timed +def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with pg_client.PostgresClient() as cur: + row = __get_application_activity_avg_page_load_time(cur, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + row = __get_application_activity_avg_page_load_time(cur, project_id, startTimestamp, endTimestamp, **args) + previous = helper.dict_to_camel_case(row) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["chart"] = get_performance_avg_page_load_time(project_id, startTimestamp, endTimestamp, **args) + return results + + +@dev.timed +def get_performance_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), + density=19, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) + location_constraints = [] + location_constraints_vals = {} + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + with pg_client.PostgresClient() as cur: + pg_sub_query_subset = __get_constraints(project_id=project_id, time_constraint=True, + chart=False, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=False, project=False, + chart=True, data=args, main_table="pages", time_column="timestamp", + duration=False) + pg_sub_query_subset.append("pages.timestamp >= %(startTimestamp)s") + pg_sub_query_subset.append("pages.timestamp < %(endTimestamp)s") + pg_query = f"""WITH pages AS(SELECT pages.load_time, timestamp + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_subset)} AND pages.load_time>0 AND pages.load_time IS NOT NULL + {(f' AND ({" OR ".join(location_constraints)})') if len(location_constraints) > 0 else ""} + ) + SELECT + generated_timestamp AS timestamp, + COALESCE(AVG(pages.load_time),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( SELECT pages.load_time + FROM pages + WHERE {" AND ".join(pg_sub_query_chart)} + {(f' AND ({" OR ".join(location_constraints)})') if len(location_constraints) > 0 else ""} + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp;""" + cur.execute(cur.mogrify(pg_query, {**params, **location_constraints_vals, **__get_constraint_values(args)})) + rows = cur.fetchall() + return rows + + +def __get_application_activity_avg_request_load_time(cur, project_id, startTimestamp, endTimestamp, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query.append("resources.duration > 0") + pg_sub_query.append("resources.type= %(type)s") + pg_query = f"""\ + SELECT COALESCE(AVG(resources.duration),0) AS value + FROM events.resources INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)};""" + + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "type": 'img', "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)})) + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "type": 'fetch', "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)})) + + row = cur.fetchone() + return row + + +@dev.timed +def get_application_activity_avg_request_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with pg_client.PostgresClient() as cur: + row = __get_application_activity_avg_request_load_time(cur, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + row = __get_application_activity_avg_request_load_time(cur, project_id, startTimestamp, endTimestamp, **args) + previous = helper.dict_to_camel_case(row) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["chart"] = get_performance_avg_request_load_time(project_id, startTimestamp, endTimestamp, **args) + return results + + +@dev.timed +def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), + density=19, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) + location_constraints = [] + img_constraints = [] + request_constraints = [] + + img_constraints_vals = {} + location_constraints_vals = {} + request_constraints_vals = {} + + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + with pg_client.PostgresClient() as cur: + pg_sub_query_subset = __get_constraints(project_id=project_id, time_constraint=True, + chart=False, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=False, project=False, + chart=True, data=args, main_table="resources", time_column="timestamp", + duration=False) + pg_sub_query_subset.append("resources.timestamp >= %(startTimestamp)s") + pg_sub_query_subset.append("resources.timestamp < %(endTimestamp)s") + + pg_query = f"""WITH resources AS(SELECT resources.duration, resources.timestamp + FROM events.resources INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_subset)} + AND resources.type = 'fetch' AND resources.duration>0 + {(f' AND ({" OR ".join(request_constraints)})') if len(request_constraints) > 0 else ""} + ) + SELECT + generated_timestamp AS timestamp, + COALESCE(AVG(resources.duration),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT resources.duration + FROM resources + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS resources ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp;""" + cur.execute(cur.mogrify(pg_query, {**params, **request_constraints_vals, **__get_constraint_values(args)})) + rows = cur.fetchall() + + return rows diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 169893693..4faa82d62 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -344,3 +344,30 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body *helper.explode_widget(dashboard.get_avg_cpu(project_id=projectId, **data.dict())), *helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())), ]} + +@app.post('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"]) +@app.get('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"]) +def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): + return {"data": [ + {"key": schemas.TemplateKeys.count_sessions, + "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_image_load_time, + "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_page_load_time, + "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_request_load_time, + "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, + + # *helper.explode_widget(data=dashboard.get_page_metrics(project_id=projectId, **data.dict())), + # *helper.explode_widget(data=dashboard.get_user_activity(project_id=projectId, **data.dict())), + # *helper.explode_widget(data=dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict()), + # key="avg_pages_dom_buildtime"), + # *helper.explode_widget(data=dashboard.get_pages_response_time(project_id=projectId, **data.dict()), + # key="avg_pages_response_time"), + # *helper.explode_widget(dashboard.get_top_metrics(project_id=projectId, **data.dict())), + # *helper.explode_widget(data=dashboard.get_time_to_render(project_id=projectId, **data.dict()), + # key="avg_time_to_render"), + # *helper.explode_widget(dashboard.get_memory_consumption(project_id=projectId, **data.dict())), + # *helper.explode_widget(dashboard.get_avg_cpu(project_id=projectId, **data.dict())), + # *helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())), + ]} diff --git a/api/schemas.py b/api/schemas.py index ad9558a4a..b2b96d048 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -900,3 +900,11 @@ class AddWidgetToDashboardPayloadSchema(BaseModel): class Config: alias_generator = attribute_to_camel_case + + +# these values should match the keys in metrics table +class TemplateKeys(str, Enum): + count_sessions = "count_sessions" + avg_request_load_time = "avg_request_load_time" + avg_page_load_time = "avg_page_load_time" + avg_image_load_time = "avg_image_load_time" From b3018f9f76617a793863cbb986e918a47eb0f78f Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 30 Mar 2022 10:49:45 +0200 Subject: [PATCH 26/87] feat(api): dashboard split old metrics grouped data 2/3 --- api/chalicelib/core/dashboard.py | 138 ++++++++++++++++++++++++++++++- api/routers/subs/dashboard.py | 48 ++++++----- api/schemas.py | 6 ++ 3 files changed, 168 insertions(+), 24 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 72995c27a..62bdc5466 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -992,13 +992,13 @@ def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=- FROM public.sessions INNER JOIN events.pages USING (session_id) WHERE {" AND ".join(pg_sub_query_subset)}) - SELECT COALESCE(avg, 0) AS avg, chart + SELECT COALESCE(avg, 0) AS value, chart FROM (SELECT AVG(dom_building_time) FROM pages) AS avg LEFT JOIN (SELECT jsonb_agg(chart) AS chart FROM ( SELECT generated_timestamp AS timestamp, - COALESCE(AVG(dom_building_time), 0) AS avg + COALESCE(AVG(dom_building_time), 0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT pages.dom_building_time FROM pages @@ -1154,7 +1154,7 @@ def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1 pg_sub_query_chart.append(f"url = %(value)s") with pg_client.PostgresClient() as cur: pg_query = f"""SELECT generated_timestamp AS timestamp, - COALESCE(AVG(pages.response_time),0) AS avg + COALESCE(AVG(pages.response_time),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT response_time @@ -1175,7 +1175,7 @@ def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1 WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] - return {"avg": avg, "chart": rows} + return {"value": avg, "chart": rows} @dev.timed @@ -2509,3 +2509,133 @@ def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now rows = cur.fetchall() return rows + + +def get_page_metrics_avg_dom_content_load_start(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with pg_client.PostgresClient() as cur: + rows = __get_page_metrics_avg_dom_content_load_start(cur, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + results = helper.dict_to_camel_case(rows[0]) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + rows = __get_page_metrics_avg_dom_content_load_start(cur, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + previous = helper.dict_to_camel_case(rows[0]) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_page_metrics_avg_dom_content_load_start(cur, project_id, startTimestamp, endTimestamp, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query.append("pages.timestamp>=%(startTimestamp)s") + pg_sub_query.append("pages.timestamp<%(endTimestamp)s") + pg_sub_query.append("pages.dom_content_loaded_time > 0") + pg_query = f"""SELECT COALESCE(AVG(NULLIF(pages.dom_content_loaded_time, 0)), 0) AS value + FROM (SELECT pages.dom_content_loaded_time + FROM events.pages + INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)} + ) AS pages;""" + params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + return rows + + +def get_page_metrics_avg_first_contentful_pixel(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with pg_client.PostgresClient() as cur: + rows = __get_page_metrics_avg_first_contentful_pixel(cur, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + results = helper.dict_to_camel_case(rows[0]) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + rows = __get_page_metrics_avg_first_contentful_pixel(cur, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + previous = helper.dict_to_camel_case(rows[0]) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_page_metrics_avg_first_contentful_pixel(cur, project_id, startTimestamp, endTimestamp, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query.append("pages.timestamp>=%(startTimestamp)s") + pg_sub_query.append("pages.timestamp<%(endTimestamp)s") + pg_sub_query.append("pages.first_contentful_paint_time > 0") + pg_query = f"""SELECT COALESCE(AVG(NULLIF(pages.first_contentful_paint_time, 0)), 0) AS value + FROM (SELECT pages.first_contentful_paint_time + FROM events.pages + INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)} + ) AS pages;""" + params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + return rows + + + + +def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with pg_client.PostgresClient() as cur: + row = __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + row = __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTimestamp, **args) + + previous = helper.dict_to_camel_case(row) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTimestamp, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + + pg_query = f"""\ + SELECT COALESCE(CEIL(AVG(NULLIF(sessions.pages_count,0))),0) AS value + FROM public.sessions + WHERE {" AND ".join(pg_sub_query)};""" + params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return row + + +def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with pg_client.PostgresClient() as cur: + row = __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + row = __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, endTimestamp, **args) + + previous = helper.dict_to_camel_case(row) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, endTimestamp, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + + pg_query = f"""\ + SELECT COALESCE(AVG(NULLIF(sessions.duration,0)),0) AS value + FROM public.sessions + WHERE {" AND ".join(pg_sub_query)};""" + params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return row diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 4faa82d62..e0b680362 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -326,17 +326,18 @@ def get_dashboard_resources_count_by_type(projectId: int, data: schemas.MetricPa @app.get('/{projectId}/dashboard/overview', tags=["dashboard", "metrics"]) def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): return {"data": [ - *helper.explode_widget(key="count_sessions", - data=dashboard.get_processed_sessions(project_id=projectId, **data.dict())), + {"key": "count_sessions", + "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, *helper.explode_widget(data={**dashboard.get_application_activity(project_id=projectId, **data.dict()), "chart": dashboard.get_performance(project_id=projectId, **data.dict()) .get("chart", [])}), *helper.explode_widget(data=dashboard.get_page_metrics(project_id=projectId, **data.dict())), *helper.explode_widget(data=dashboard.get_user_activity(project_id=projectId, **data.dict())), - *helper.explode_widget(data=dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict()), - key="avg_pages_dom_buildtime"), - *helper.explode_widget(data=dashboard.get_pages_response_time(project_id=projectId, **data.dict()), - key="avg_pages_response_time"), + {"key": "avg_pages_dom_buildtime", + "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, + {"key": "avg_pages_response_time", + "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict()) + }, *helper.explode_widget(dashboard.get_top_metrics(project_id=projectId, **data.dict())), *helper.explode_widget(data=dashboard.get_time_to_render(project_id=projectId, **data.dict()), key="avg_time_to_render"), @@ -345,25 +346,32 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body *helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())), ]} + @app.post('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"]) def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): return {"data": [ - {"key": schemas.TemplateKeys.count_sessions, - "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_image_load_time, - "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_page_load_time, - "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_request_load_time, - "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplateKeys.count_sessions, + # "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplateKeys.avg_image_load_time, + # "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplateKeys.avg_page_load_time, + # "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplateKeys.avg_request_load_time, + # "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplateKeys.avg_dom_content_load_start, + # "data": dashboard.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplateKeys.avg_first_contentful_pixel, + # "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())} + # {"key": schemas.TemplateKeys.avg_visited_pages, + # "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplateKeys.avg_session_duration, + # "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())} + # {"key": schemas.TemplateKeys.avg_pages_dom_buildtime, + # "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, - # *helper.explode_widget(data=dashboard.get_page_metrics(project_id=projectId, **data.dict())), - # *helper.explode_widget(data=dashboard.get_user_activity(project_id=projectId, **data.dict())), - # *helper.explode_widget(data=dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict()), - # key="avg_pages_dom_buildtime"), - # *helper.explode_widget(data=dashboard.get_pages_response_time(project_id=projectId, **data.dict()), - # key="avg_pages_response_time"), + *helper.explode_widget(data=dashboard.get_pages_response_time(project_id=projectId, **data.dict()), + key="avg_pages_response_time"), # *helper.explode_widget(dashboard.get_top_metrics(project_id=projectId, **data.dict())), # *helper.explode_widget(data=dashboard.get_time_to_render(project_id=projectId, **data.dict()), # key="avg_time_to_render"), diff --git a/api/schemas.py b/api/schemas.py index b2b96d048..4f672a844 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -908,3 +908,9 @@ class TemplateKeys(str, Enum): avg_request_load_time = "avg_request_load_time" avg_page_load_time = "avg_page_load_time" avg_image_load_time = "avg_image_load_time" + avg_dom_content_load_start = "avg_dom_content_load_start" + avg_first_contentful_pixel = "avg_first_contentful_pixel" + avg_visited_pages = "avg_visited_pages" + avg_session_duration = "avg_session_duration" + avg_pages_dom_buildtime="avg_pages_dom_buildtime" + avg_pages_response_time="avg_pages_response_time" From d6c66e6d464bc114a520d6f25866a245e3b5b731 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 30 Mar 2022 10:53:37 +0200 Subject: [PATCH 27/87] workflow_dispatch --- .github/workflows/api.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/api.yaml b/.github/workflows/api.yaml index eb193dbb7..9fe8c5611 100644 --- a/.github/workflows/api.yaml +++ b/.github/workflows/api.yaml @@ -1,5 +1,6 @@ # This action will push the chalice changes to aws on: + workflow_dispatch: push: branches: - api-v1.5.5 From 3b6085606307aee70966bf74ef29fcf5ab01293c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 30 Mar 2022 12:29:06 +0200 Subject: [PATCH 28/87] feat(api): dashboard split old metrics grouped data 3/3 --- api/chalicelib/core/dashboard.py | 197 ++++++++++++------ api/routers/subs/dashboard.py | 74 ++++--- api/schemas.py | 14 +- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 22 +- 4 files changed, 203 insertions(+), 104 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 62bdc5466..bab19ba12 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -127,7 +127,6 @@ SESSIONS_META_FIELDS = {"revId": "rev_id", "browser": "user_browser"} -@dev.timed def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): @@ -175,7 +174,6 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) return results -@dev.timed def get_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -234,7 +232,6 @@ def __count_distinct_errors(cur, project_id, startTimestamp, endTimestamp, pg_su return cur.fetchone()["count"] -@dev.timed def get_errors_trend(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): @@ -298,7 +295,6 @@ def get_errors_trend(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return rows -@dev.timed def get_page_metrics(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: @@ -316,7 +312,6 @@ def get_page_metrics(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return results -@dev.timed def __get_page_metrics(cur, project_id, startTimestamp, endTimestamp, **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) pg_sub_query.append("pages.timestamp>=%(startTimestamp)s") @@ -336,7 +331,6 @@ def __get_page_metrics(cur, project_id, startTimestamp, endTimestamp, **args): return rows -@dev.timed def get_application_activity(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: @@ -390,7 +384,6 @@ def __get_application_activity(cur, project_id, startTimestamp, endTimestamp, ** return result -@dev.timed def get_user_activity(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: @@ -423,7 +416,6 @@ def __get_user_activity(cur, project_id, startTimestamp, endTimestamp, **args): return row -@dev.timed def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): @@ -479,7 +471,6 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return sorted(rows, key=lambda k: k["sessions"], reverse=True) -@dev.timed def __get_performance_constraint(l): if len(l) == 0: return "" @@ -487,7 +478,6 @@ def __get_performance_constraint(l): return f"AND ({' OR '.join(l)})" -@dev.timed def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=19, resources=None, **args): step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) @@ -622,7 +612,6 @@ def __get_resource_db_type_from_type(resource_type): return {v: k for k, v in RESOURCS_TYPE_TO_DB_TYPE.items()}.get(resource_type, resource_type) -@dev.timed def search(text, resource_type, project_id, performance=False, pages_only=False, events_only=False, metadata=False, key=None, platform=None): if not resource_type: @@ -799,7 +788,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, return [helper.dict_to_camel_case(row) for row in rows] -@dev.timed def get_missing_resources_trend(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): @@ -855,7 +843,6 @@ def get_missing_resources_trend(project_id, startTimestamp=TimeUTC.now(delta_day return rows -@dev.timed def get_network(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): @@ -921,7 +908,6 @@ def dashboard_args(params): return args -@dev.timed def get_resources_loading_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=19, type=None, url=None, **args): @@ -970,7 +956,6 @@ def get_resources_loading_time(project_id, startTimestamp=TimeUTC.now(delta_days return {"avg": avg, "chart": rows} -@dev.timed def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=19, url=None, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1016,7 +1001,6 @@ def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=- return row -@dev.timed def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), type="all", density=19, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1090,7 +1074,6 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return rows -@dev.timed def get_sessions_location(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1109,7 +1092,6 @@ def get_sessions_location(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return {"count": sum(i["count"] for i in rows), "chart": helper.list_to_camel_case(rows)} -@dev.timed def get_speed_index_location(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1138,7 +1120,6 @@ def get_speed_index_location(project_id, startTimestamp=TimeUTC.now(delta_days=- return {"avg": avg, "chart": helper.list_to_camel_case(rows)} -@dev.timed def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, url=None, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1178,7 +1159,6 @@ def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1 return {"value": avg, "chart": rows} -@dev.timed def get_pages_response_time_distribution(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=20, **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1296,7 +1276,6 @@ def get_pages_response_time_distribution(project_id, startTimestamp=TimeUTC.now( return result -@dev.timed def get_busiest_time_of_day(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1316,7 +1295,6 @@ def get_busiest_time_of_day(project_id, startTimestamp=TimeUTC.now(delta_days=-1 return rows -@dev.timed def get_top_metrics(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), value=None, **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1367,7 +1345,6 @@ def get_top_metrics(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return helper.dict_to_camel_case(row) -@dev.timed def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, url=None, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1383,11 +1360,11 @@ def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), pg_query = f"""WITH pages AS(SELECT pages.visually_complete,pages.timestamp FROM events.pages INNER JOIN public.sessions USING (session_id) WHERE {" AND ".join(pg_sub_query_subset)}) - SELECT COALESCE((SELECT AVG(pages.visually_complete) FROM pages),0) AS avg, + SELECT COALESCE((SELECT AVG(pages.visually_complete) FROM pages),0) AS value, jsonb_agg(chart) AS chart FROM (SELECT generated_timestamp AS timestamp, - COALESCE(AVG(visually_complete), 0) AS avg + COALESCE(AVG(visually_complete), 0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT pages.visually_complete FROM pages @@ -1404,7 +1381,6 @@ def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return row -@dev.timed def get_impacted_sessions_by_slow_pages(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), value=None, density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1443,7 +1419,6 @@ def get_impacted_sessions_by_slow_pages(project_id, startTimestamp=TimeUTC.now(d return rows -@dev.timed def get_memory_consumption(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1453,7 +1428,7 @@ def get_memory_consumption(project_id, startTimestamp=TimeUTC.now(delta_days=-1) with pg_client.PostgresClient() as cur: pg_query = f"""SELECT generated_timestamp AS timestamp, - COALESCE(AVG(performance.avg_used_js_heap_size),0) AS avg_used_js_heap_size + COALESCE(AVG(performance.avg_used_js_heap_size),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT avg_used_js_heap_size @@ -1473,10 +1448,9 @@ def get_memory_consumption(project_id, startTimestamp=TimeUTC.now(delta_days=-1) WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] - return {"avgUsedJsHeapSize": avg, "chart": helper.list_to_camel_case(rows)} + return {"value": avg, "chart": helper.list_to_camel_case(rows)} -@dev.timed def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1486,7 +1460,7 @@ def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with pg_client.PostgresClient() as cur: pg_query = f"""SELECT generated_timestamp AS timestamp, - COALESCE(AVG(performance.avg_cpu),0) AS avg_cpu + COALESCE(AVG(performance.avg_cpu),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT avg_cpu @@ -1506,10 +1480,9 @@ def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] - return {"avgCpu": avg, "chart": helper.list_to_camel_case(rows)} + return {"value": avg, "chart": helper.list_to_camel_case(rows)} -@dev.timed def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1519,7 +1492,7 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with pg_client.PostgresClient() as cur: pg_query = f"""SELECT generated_timestamp AS timestamp, - COALESCE(AVG(NULLIF(performance.avg_fps,0)),0) AS avg_fps + COALESCE(AVG(NULLIF(performance.avg_fps,0)),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT avg_fps @@ -1539,10 +1512,9 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] - return {"avgFps": avg, "chart": helper.list_to_camel_case(rows)} + return {"value": avg, "chart": helper.list_to_camel_case(rows)} -@dev.timed def get_crashes(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1627,7 +1599,6 @@ def __merge_rows_with_neutral(rows, neutral): return rows -@dev.timed def get_domains_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=6, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1678,7 +1649,6 @@ def get_domains_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return result -@dev.timed def get_domains_errors_4xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=6, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1719,7 +1689,6 @@ def get_domains_errors_4xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1) return rows -@dev.timed def get_domains_errors_5xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=6, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -1768,7 +1737,6 @@ def __nested_array_to_dict_array(rows, key="url_host", value="count"): return rows -@dev.timed def get_slowest_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1800,7 +1768,6 @@ def get_slowest_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return {"avg": avg, "partition": rows} -@dev.timed def get_errors_per_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1822,7 +1789,6 @@ def get_errors_per_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1) return helper.list_to_camel_case(rows) -@dev.timed def get_sessions_per_browser(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), platform=None, **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1865,7 +1831,6 @@ def get_sessions_per_browser(project_id, startTimestamp=TimeUTC.now(delta_days=- return {"count": sum(i["count"] for i in rows), "chart": rows} -@dev.timed def get_calls_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), platform=None, **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1891,7 +1856,6 @@ def get_calls_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endT return helper.list_to_camel_case(rows) -@dev.timed def get_calls_errors_4xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), platform=None, **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1915,7 +1879,6 @@ def get_calls_errors_4xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return helper.list_to_camel_case(rows) -@dev.timed def get_calls_errors_5xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), platform=None, **args): pg_sub_query = __get_constraints(project_id=project_id, data=args) @@ -1939,7 +1902,6 @@ def get_calls_errors_5xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return helper.list_to_camel_case(rows) -@dev.timed def get_errors_per_type(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), platform=None, density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -2004,7 +1966,6 @@ def get_errors_per_type(project_id, startTimestamp=TimeUTC.now(delta_days=-1), e return rows -@dev.timed def resource_type_vs_response_end(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -2059,7 +2020,6 @@ def resource_type_vs_response_end(project_id, startTimestamp=TimeUTC.now(delta_d return helper.list_to_camel_case(__merge_charts(response_end, actions)) -@dev.timed def get_impacted_sessions_by_js_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -2141,7 +2101,6 @@ def get_impacted_sessions_by_js_errors(project_id, startTimestamp=TimeUTC.now(de return {**row_sessions, **row_errors, "chart": chart} -@dev.timed def get_resources_vs_visually_complete(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -2192,7 +2151,6 @@ def get_resources_vs_visually_complete(project_id, startTimestamp=TimeUTC.now(de return helper.list_to_camel_case(rows) -@dev.timed def get_resources_count_by_type(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -2229,7 +2187,6 @@ def get_resources_count_by_type(project_id, startTimestamp=TimeUTC.now(delta_day return rows -@dev.timed def get_resources_by_party(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) @@ -2376,7 +2333,6 @@ def __get_application_activity_avg_page_load_time(cur, project_id, startTimestam return row -@dev.timed def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: @@ -2392,7 +2348,6 @@ def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeU return results -@dev.timed def get_performance_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=19, **args): @@ -2448,7 +2403,6 @@ def __get_application_activity_avg_request_load_time(cur, project_id, startTimes return row -@dev.timed def get_application_activity_avg_request_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: @@ -2464,7 +2418,6 @@ def get_application_activity_avg_request_load_time(project_id, startTimestamp=Ti return results -@dev.timed def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=19, **args): @@ -2579,10 +2532,8 @@ def __get_page_metrics_avg_first_contentful_pixel(cur, project_id, startTimestam return rows - - def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), **args): + endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: row = __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTimestamp, **args) results = helper.dict_to_camel_case(row) @@ -2612,7 +2563,7 @@ def __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTi def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), **args): + endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: row = __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, endTimestamp, **args) results = helper.dict_to_camel_case(row) @@ -2639,3 +2590,131 @@ def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, en cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() return row + + +def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + + if value is not None: + pg_sub_query.append("pages.path = %(value)s") + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT COALESCE(AVG(pages.response_time), 0) AS value + FROM events.pages + INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)} + AND pages.timestamp >= %(startTimestamp)s + AND pages.timestamp < %(endTimestamp)s + AND pages.response_time > 0;""" + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)})) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + +def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + + if value is not None: + pg_sub_query.append("pages.path = %(value)s") + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT COALESCE(AVG(pages.first_paint_time), 0) AS value + FROM events.pages + INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)} + AND pages.timestamp >= %(startTimestamp)s + AND pages.timestamp < %(endTimestamp)s + AND pages.first_paint_time > 0;""" + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)})) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + +def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + + if value is not None: + pg_sub_query.append("pages.path = %(value)s") + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT COALESCE(AVG(pages.dom_content_loaded_time), 0) AS value + FROM events.pages + INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)} + AND pages.timestamp >= %(startTimestamp)s + AND pages.timestamp < %(endTimestamp)s + AND pages.dom_content_loaded_time > 0;""" + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)})) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + +def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + + if value is not None: + pg_sub_query.append("pages.path = %(value)s") + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT COALESCE(AVG(pages.ttfb), 0) AS value + FROM events.pages + INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)} + AND pages.timestamp >= %(startTimestamp)s + AND pages.timestamp < %(endTimestamp)s + AND pages.ttfb > 0;""" + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)})) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + +def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + + if value is not None: + pg_sub_query.append("pages.path = %(value)s") + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT COALESCE(AVG(pages.time_to_interactive), 0) AS value + FROM events.pages + INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)} + AND pages.timestamp >= %(startTimestamp)s + AND pages.timestamp < %(endTimestamp)s + AND pages.time_to_interactive > 0;""" + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)})) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + +def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + pg_sub_query = __get_constraints(project_id=project_id, data=args) + + if value is not None: + pg_sub_query.append("pages.path = %(value)s") + with pg_client.PostgresClient() as cur: + pg_query = f"""SELECT COUNT(pages.session_id) AS value + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)};""" + cur.execute(cur.mogrify(pg_query, {"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)})) + row = cur.fetchone() + return helper.dict_to_camel_case(row) diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index e0b680362..e81e18b5e 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -339,11 +339,10 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict()) }, *helper.explode_widget(dashboard.get_top_metrics(project_id=projectId, **data.dict())), - *helper.explode_widget(data=dashboard.get_time_to_render(project_id=projectId, **data.dict()), - key="avg_time_to_render"), - *helper.explode_widget(dashboard.get_memory_consumption(project_id=projectId, **data.dict())), - *helper.explode_widget(dashboard.get_avg_cpu(project_id=projectId, **data.dict())), - *helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())), + {"key": "avg_time_to_render", "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, + {"key": "avg_used_js_heap_size", "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, + {"key": "avg_cpu", "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_fps, "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} ]} @@ -351,31 +350,42 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body @app.get('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"]) def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): return {"data": [ - # {"key": schemas.TemplateKeys.count_sessions, - # "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplateKeys.avg_image_load_time, - # "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplateKeys.avg_page_load_time, - # "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplateKeys.avg_request_load_time, - # "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplateKeys.avg_dom_content_load_start, - # "data": dashboard.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplateKeys.avg_first_contentful_pixel, - # "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())} - # {"key": schemas.TemplateKeys.avg_visited_pages, - # "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplateKeys.avg_session_duration, - # "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())} - # {"key": schemas.TemplateKeys.avg_pages_dom_buildtime, - # "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, - - *helper.explode_widget(data=dashboard.get_pages_response_time(project_id=projectId, **data.dict()), - key="avg_pages_response_time"), - # *helper.explode_widget(dashboard.get_top_metrics(project_id=projectId, **data.dict())), - # *helper.explode_widget(data=dashboard.get_time_to_render(project_id=projectId, **data.dict()), - # key="avg_time_to_render"), - # *helper.explode_widget(dashboard.get_memory_consumption(project_id=projectId, **data.dict())), - # *helper.explode_widget(dashboard.get_avg_cpu(project_id=projectId, **data.dict())), - # *helper.explode_widget(dashboard.get_avg_fps(project_id=projectId, **data.dict())), + {"key": schemas.TemplateKeys.count_sessions, + "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_image_load_time, + "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_page_load_time, + "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_request_load_time, + "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_dom_content_load_start, + "data": dashboard.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_first_contentful_pixel, + "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_visited_pages, + "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_session_duration, + "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_pages_dom_buildtime, + "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_pages_response_time, + "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_response_time, + "data": dashboard.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_first_paint, + "data": dashboard.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_dom_content_loaded, + "data": dashboard.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_till_first_bit, + "data": dashboard.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_time_to_interactive, + "data": dashboard.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.count_requests, + "data": dashboard.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_time_to_render, + "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_used_js_heap_size, + "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_cpu, "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_fps, "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} ]} diff --git a/api/schemas.py b/api/schemas.py index 4f672a844..ef1972d98 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -912,5 +912,15 @@ class TemplateKeys(str, Enum): avg_first_contentful_pixel = "avg_first_contentful_pixel" avg_visited_pages = "avg_visited_pages" avg_session_duration = "avg_session_duration" - avg_pages_dom_buildtime="avg_pages_dom_buildtime" - avg_pages_response_time="avg_pages_response_time" + avg_pages_dom_buildtime = "avg_pages_dom_buildtime" + avg_pages_response_time = "avg_pages_response_time" + avg_response_time = "avg_response_time" + avg_first_paint = "avg_first_paint" + avg_dom_content_loaded = "avg_dom_content_loaded" + avg_till_first_bit = "avg_till_first_bit" + avg_time_to_interactive = "avg_time_to_interactive" + count_requests = "count_requests" + avg_time_to_render = "avg_time_to_render" + avg_used_js_heap_size = "avg_used_js_heap_size" + avg_cpu = "avg_cpu" + avg_fps = "avg_fps" diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 13e9e9e14..3080f8dbb 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -6,17 +6,17 @@ SELECT 'v1.5.5' $$ LANGUAGE sql IMMUTABLE; --- CREATE TABLE dashboards --- ( --- dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, --- project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, --- user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, --- name text NOT NULL, --- is_public boolean NOT NULL DEFAULT TRUE, --- is_pinned boolean NOT NULL DEFAULT FALSE, --- created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), --- deleted_at timestamp NULL DEFAULT NULL --- ); +CREATE TABLE IF NOT EXISTS dashboards +( + dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + name text NOT NULL, + is_public boolean NOT NULL DEFAULT TRUE, + is_pinned boolean NOT NULL DEFAULT FALSE, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + deleted_at timestamp NULL DEFAULT NULL +); -- CREATE TABLE templates -- ( From 7796ff8a6cce06ca10bedef5593cec0cdc112ceb Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 30 Mar 2022 18:08:09 +0200 Subject: [PATCH 29/87] feat(ui) - dashboard - wip --- frontend/app/Router.js | 2 + frontend/app/api_client.js | 2 + .../app/components/Dashboard/NewDashboard.tsx | 74 ++++--- .../DashbaordListModal/DashbaordListModal.tsx | 39 ++++ .../components/DashbaordListModal/index.ts | 1 + .../DashboardForm/DashboardForm.tsx | 7 +- .../DashboardMetricSelection.tsx | 35 ++-- .../DashboardModal/DashboardModal.tsx | 25 ++- .../DashboardSideMenu/DashboardSideMenu.tsx | 55 ++++- .../DashboardView/DashboardView.tsx | 25 +-- .../DashboardWidgetGrid.tsx | 12 +- .../components/MetricsList/MetricsList.tsx | 14 +- .../MetricsSearch/MetricsSearch.tsx | 8 +- .../components/WidgetForm/WidgetForm.tsx | 59 ++++-- .../WidgetPreview/WidgetPreview.tsx | 6 +- .../WidgetSessions/WidgetSessions.tsx | 6 +- .../components/WidgetView/WidgetView.tsx | 6 +- .../components/Dashboard/store/dashboard.ts | 42 +++- .../Dashboard/store/dashboardStore.ts | 195 +++++++++++------- .../app/components/Dashboard/store/widget.ts | 40 +++- frontend/app/components/Modal/Modal.js | 23 +-- .../app/components/Modal/ModalOverlay.css | 32 +++ .../app/components/Modal/ModalOverlay.tsx | 13 +- frontend/app/components/Modal/index.tsx | 44 ++++ frontend/app/initialize.js | 21 +- frontend/app/mstore/index.tsx | 24 +++ frontend/app/services/DashboardService.ts | 142 +++++++++++++ frontend/app/services/index.ts | 3 + 28 files changed, 713 insertions(+), 242 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx create mode 100644 frontend/app/components/Dashboard/components/DashbaordListModal/index.ts create mode 100644 frontend/app/components/Modal/ModalOverlay.css create mode 100644 frontend/app/components/Modal/index.tsx create mode 100644 frontend/app/mstore/index.tsx create mode 100644 frontend/app/services/DashboardService.ts create mode 100644 frontend/app/services/index.ts diff --git a/frontend/app/Router.js b/frontend/app/Router.js index e270afaba..b8c7f7a03 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -26,6 +26,7 @@ import { fetchList as fetchSiteList } from 'Duck/site'; import { fetchList as fetchAnnouncements } from 'Duck/announcements'; import { fetchList as fetchAlerts } from 'Duck/alerts'; import { fetchWatchdogStatus } from 'Duck/watchdogs'; +import { dashboardService } from "App/services"; import APIClient from './api_client'; import * as routes from './routes'; @@ -114,6 +115,7 @@ class Router extends React.Component { fetchInitialData = () => { Promise.all([ this.props.fetchUserInfo().then(() => { + dashboardService.initClient(); this.props.fetchIntegrationVariables() }), this.props.fetchSiteList().then(() => { diff --git a/frontend/app/api_client.js b/frontend/app/api_client.js index 7a725dd31..8fc753c08 100644 --- a/frontend/app/api_client.js +++ b/frontend/app/api_client.js @@ -23,6 +23,8 @@ const siteIdRequiredPaths = [ '/assist', '/heatmaps', '/custom_metrics', + '/dashboards', + '/metrics' // '/custom_metrics/sessions', ]; diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index f4b6aec3e..832af0df0 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { Switch, Route, Redirect } from 'react-router'; import withPageTitle from 'HOCs/withPageTitle'; import { observer } from "mobx-react-lite"; -import { useDashboardStore } from './store/store'; +import { useStore } from 'App/mstore'; import { withRouter } from 'react-router-dom'; import DashboardView from './components/DashboardView'; import { @@ -18,71 +18,67 @@ import MetricsView from './components/MetricsView'; function NewDashboard(props) { const { history, match: { params: { siteId, dashboardId, metricId } } } = props; - const store: any = useDashboardStore(); - const dashboard = store.selectedDashboard; + const { dashboardStore } = useStore(); + const dashboard: any = dashboardStore.selectedDashboard; useEffect(() => { - store.setSiteId(siteId); + dashboardStore.fetchList(); + dashboardStore.setSiteId(siteId); }, []); useEffect(() => { if (dashboardId) { - store.selectDashboardById(dashboardId); + dashboardStore.selectDashboardById(dashboardId); } if (!dashboardId) { if (dashboardId) { - store.selectDashboardById(dashboardId); + dashboardStore.selectDashboardById(dashboardId); } else { - store.selectDefaultDashboard().then((resp) => { + dashboardStore.selectDefaultDashboard().then((resp: any) => { history.push(withSiteId(dashboardSelected(resp.dashboardId), siteId)); }); } } }, []); - // console.log('rendering dashboard', props.match.params); return ( - <> - {/* { dashboard && dashboard.dashboardId && ( */} - - + + +
+
+ +
+
+ +
+
+
+ + + + { dashboardId && ( + <> +
- +
- + + + {/* + - - { dashboardId && ( - <> - -
-
- -
-
- -
-
-
- - - {/* - - - */} - {/* */} - - )} -
- {/* )} */} - +
*/} + {/* */} + + )} +
); } diff --git a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx new file mode 100644 index 000000000..49cef58f0 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Modal from 'react-modal'; +import { useStore } from 'App/mstore'; +import { SideMenuitem, SideMenuHeader, Icon, Button } from 'UI'; + +function DashbaordListModal(props) { + const { dashboardStore } = useStore(); + const dashboards = dashboardStore.dashboards; + const activeDashboardId = dashboardStore.selectedDashboard?.dashboardId; + return ( +
+
Dashboards
+
+ {dashboards.map((item: any) => ( + //
+ // {item.name} + //
+
+ onItemClick(item)} + leading = {( +
+
+ {item.isPinned &&
} +
+ )} + /> +
+ ))} +
+
+ ); +} + +export default DashbaordListModal; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashbaordListModal/index.ts b/frontend/app/components/Dashboard/components/DashbaordListModal/index.ts new file mode 100644 index 000000000..2948a8225 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashbaordListModal/index.ts @@ -0,0 +1 @@ +export { default } from './DashbaordListModal' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardForm/DashboardForm.tsx b/frontend/app/components/Dashboard/components/DashboardForm/DashboardForm.tsx index 9765424eb..9255663e9 100644 --- a/frontend/app/components/Dashboard/components/DashboardForm/DashboardForm.tsx +++ b/frontend/app/components/Dashboard/components/DashboardForm/DashboardForm.tsx @@ -3,13 +3,14 @@ import React from 'react'; import { Input } from 'UI'; import { useDashboardStore } from '../../store/store'; import cn from 'classnames'; +import { useStore } from 'App/mstore'; interface Props { } -function DashboardForm(props) { - const store: any = useDashboardStore(); - const dashboard = store.newDashboard; +function DashboardForm(props: Props) { + const { dashboardStore } = useStore(); + const dashboard = dashboardStore.dashboardInstance; const write = ({ target: { value, name } }) => dashboard.update({ [ name ]: value }) const writeRadio = ({ target: { value, name } }) => { diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index 08984e96e..d7c7cfa05 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -4,6 +4,7 @@ import { useDashboardStore } from '../../store/store'; import { useObserver } from 'mobx-react-lite'; import cn from 'classnames'; import { Button } from 'UI'; +import { useStore } from 'App/mstore'; function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, unSelectCategory }) { const selectedCategoryWidgetsCount = useObserver(() => { @@ -27,9 +28,9 @@ function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, } function DashboardMetricSelection(props) { - const store: any = useDashboardStore(); - const widgetCategories = store?.widgetCategories; - const widgetTemplates = store?.widgetTemplates; + const { dashboardStore } = useStore(); + const widgetCategories = dashboardStore?.widgetCategories; + const widgetTemplates = dashboardStore?.widgetTemplates; const [activeCategory, setActiveCategory] = React.useState(widgetCategories[0]); const [selectedWidgets, setSelectedWidgets] = React.useState([]); const selectedWidgetIds = selectedWidgets.map((widget: any) => widget.widgetId); @@ -73,18 +74,22 @@ function DashboardMetricSelection(props) {
-
-

Errors Tracking

- 12 -
+ {activeCategory && ( + <> +
+

{activeCategory.name}

+ {activeCategory.widgets.length} +
-
- Showing past 7 days data for visual clue -
- - Select All -
-
+
+ Showing past 7 days data for visual clue +
+ + Select All +
+
+ + )}
@@ -104,7 +109,7 @@ function DashboardMetricSelection(props) {
- {activeCategory.widgets.map((widget: any) => ( + {activeCategory && activeCategory.widgets.map((widget: any) => (
store.newDashboard); + const { dashboardStore } = useStore(); + const { hideModal } = useModal(); + const dashbaord = useObserver(() => dashboardStore.dashboardInstance); + const loading = useObserver(() => dashboardStore.isSaving); + + const onSave = () => { + dashboardStore.save(dashbaord).then(hideModal) + } return useObserver(() => ( -
+

Create Dashboard

@@ -20,7 +30,12 @@ function DashboardModal(props) {
-
diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 5a02b25b7..646284d9e 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -1,35 +1,51 @@ import { useObserver, observer, useLocalObservable } from 'mobx-react-lite'; import React from 'react'; -import { SideMenuitem, SideMenuHeader, Icon } from 'UI'; -import { withDashboardStore } from '../../store/store'; +import { SideMenuitem, SideMenuHeader, Icon, Button } from 'UI'; +import { useStore } from 'App/mstore'; import { withRouter } from 'react-router-dom'; import { withSiteId, dashboardSelected, dashboardMetrics } from 'App/routes'; +import { useModal } from 'App/components/Modal'; +import DashbaordListModal from '../DashbaordListModal'; +import DashboardModal from '../DashboardModal'; +const SHOW_COUNT = 5; function DashboardSideMenu(props) { - const { store, history } = props; - const { dashboardId } = store.selectedDashboard; + const { hideModal, showModal } = useModal(); + const { history } = props; + const { dashboardStore } = useStore(); + const dashboardId = dashboardStore.selectedDashboard?.dashboardId; + const dashboardsPicked = dashboardStore.dashboards.slice(0, SHOW_COUNT); + const remainingDashboardsCount = dashboardStore.dashboards.length - SHOW_COUNT; + + // React.useEffect(() => { + // showModal(, {}); + // }, []); const redirect = (path) => { history.push(path); } const onItemClick = (dashboard) => { - store.selectDashboardById(dashboard.dashboardId); - const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(store.siteId)); + dashboardStore.selectDashboardById(dashboard.dashboardId); + const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(dashboardStore.siteId)); history.push(path); }; + const onAddDashboardClick = (e) => { + dashboardStore.initDashboard(); + showModal(, {}) + } + return (
- {store.dashboards.map(item => ( + {dashboardsPicked.map((item: any) => ( onItemClick(item)} - leading = {(
@@ -38,13 +54,32 @@ function DashboardSideMenu(props) { )} /> ))} +
+ {remainingDashboardsCount > 0 && ( +
showModal(, {})} + > + {remainingDashboardsCount} More +
+ )} +
+
+
+ +
redirect(withSiteId(dashboardMetrics(), store.siteId))} + onClick={() => redirect(withSiteId(dashboardMetrics(), dashboardStore.siteId))} />
@@ -60,4 +95,4 @@ function DashboardSideMenu(props) { ); } -export default withDashboardStore(withRouter(observer(DashboardSideMenu))); \ No newline at end of file +export default withRouter(observer(DashboardSideMenu)); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index ad2381b35..c229ae104 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -1,27 +1,24 @@ -import React, { useEffect } from 'react'; -import WidgetWrapper from '../../WidgetWrapper'; +import React from 'react'; import { observer } from 'mobx-react-lite'; -import { withDashboardStore } from '../../store/store'; +import { useStore } from 'App/mstore'; import { Button, PageTitle, Link } from 'UI'; import { withSiteId, dashboardMetricCreate } from 'App/routes'; import withModal from 'App/components/Modal/withModal'; -import DashboardModal from '../DashboardModal' import DashboardWidgetGrid from '../DashboardWidgetGrid'; -function DashboardView(props) { - // let { handleModal } = React.useContext(ModalContext); - const { store } = props; - const dashboard = store.selectedDashboard - const list = dashboard?.widgets; - useEffect(() => { - // props.showModal(DashboardModal) - }, []) +interface Props { + +} +function DashboardView(props: Props) { + const { dashboardStore } = useStore(); + const dashboard: any = dashboardStore.selectedDashboard + return (
- +
Right @@ -32,4 +29,4 @@ function DashboardView(props) { ) } -export default withDashboardStore(withModal(observer(DashboardView))); \ No newline at end of file +export default withModal(observer(DashboardView)); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx index 1b1256793..237d5fcb4 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx @@ -1,15 +1,15 @@ import React from 'react'; -import { useDashboardStore } from '../../store/store'; +import { useStore } from 'App/mstore'; import WidgetWrapper from '../../WidgetWrapper'; import { NoContent, Button, Loader } from 'UI'; import { useObserver } from 'mobx-react-lite'; -// import { divider } from '../../Filters/filters.css'; function DashboardWidgetGrid(props) { - const store: any = useDashboardStore(); - const loading = store.isLoading; - const dashbaord = store.selectedDashboard; - const list = dashbaord.widgets; + const { dashboardStore } = useStore(); + const loading = useObserver(() => dashboardStore.isLoading); + const dashbaord: any = dashboardStore.selectedDashboard; + const list: any = dashbaord?.widgets; + return useObserver(() => ( store.metricsPage); - const metricsSearch = useObserver(() => store.metricsSearch); + const currentPage = useObserver(() => dashboardStore.metricsPage); + const metricsSearch = useObserver(() => dashboardStore.metricsSearch); const filterRE = getRE(metricsSearch, 'i'); const list = widgets.filter(w => filterRE.test(w.name)) const totalPages = list.length; - const pageSize = store.metricsPageSize; + const pageSize = dashboardStore.metricsPageSize; const start = (currentPage - 1) * pageSize; const end = currentPage * pageSize; @@ -64,7 +64,7 @@ function MetricsList(props: Props) { store.updateKey('metricsPage', page)} + onPageChange={(page) => dashboardStore.updateKey('metricsPage', page)} limit={pageSize} debounceRequest={100} /> diff --git a/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx index 45c209350..ce4bdbe24 100644 --- a/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx +++ b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx @@ -1,11 +1,11 @@ import { useObserver } from 'mobx-react-lite'; import React from 'react'; -import { useDashboardStore } from '../../store/store'; +import { useStore } from 'App/mstore'; import { Icon } from 'UI'; function MetricsSearch(props) { - const store: any = useDashboardStore(); - const metricsSearch = useObserver(() => store.metricsSearch); + const { dashboardStore } = useStore(); + const metricsSearch = useObserver(() => dashboardStore.metricsSearch); return useObserver(() => ( @@ -16,7 +16,7 @@ function MetricsSearch(props) { name="metricsSearch" className="bg-white p-2 border rounded w-full pl-10" placeholder="Filter by title, type, dashboard and owner" - onChange={({ target: { name, value } }) => store.updateKey(name, value)} + onChange={({ target: { name, value } }) => dashboardStore.updateKey(name, value)} />
)); diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 631a3b791..9cd0002dc 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -2,20 +2,21 @@ import React from 'react'; import DropdownPlain from 'Shared/DropdownPlain'; import { metricTypes, metricOf, issueOptions } from 'App/constants/filterOptions'; import { FilterKey } from 'Types/filter/filterType'; -import { useDashboardStore } from '../../store/store'; +import { useStore } from 'App/mstore'; import { useObserver } from 'mobx-react-lite'; import { HelpText, Button, Icon } from 'UI' import FilterSeries from '../FilterSeries'; +import { withRouter } from 'react-router-dom'; interface Props { - // metric: any, - // editWidget: (metric, shouldFetch?) => void + history: any; + match: any; } function WidgetForm(props: Props) { - // const { metric } = props; - const store: any = useDashboardStore(); - const metric = store.currentWidget; + const { history, match: { params: { siteId, dashboardId, metricId } } } = props; + const { dashboardStore } = useStore(); + const metric: any = dashboardStore.currentWidget; const timeseriesOptions = metricOf.filter(i => i.type === 'timeseries'); const tableOptions = metricOf.filter(i => i.type === 'table'); @@ -23,28 +24,32 @@ function WidgetForm(props: Props) { const isTimeSeries = metric.metricType === 'timeseries'; const _issueOptions = [{ text: 'All', value: 'all' }].concat(issueOptions); - const write = ({ target: { value, name } }) => store.editWidget({ [ name ]: value }, false); + const write = ({ target: { value, name } }) => dashboardStore.editWidget({ [ name ]: value }); const writeOption = (e, { value, name }) => { - store.editWidget({ [ name ]: value }, false); + dashboardStore.editWidget({ [ name ]: value }); if (name === 'metricValue') { - store.editWidget({ metricValue: [value] }, false); + dashboardStore.editWidget({ metricValue: [value] }); } if (name === 'metricOf') { if (value === FilterKey.ISSUE) { - store.editWidget({ metricValue: ['all'] }, false); + dashboardStore.editWidget({ metricValue: ['all'] }); } } if (name === 'metricType') { if (value === 'timeseries') { - store.editWidget({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }, false); + dashboardStore.editWidget({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }); } else if (value === 'table') { - store.editWidget({ metricOf: tableOptions[0].value, viewType: 'table' }, false); + dashboardStore.editWidget({ metricOf: tableOptions[0].value, viewType: 'table' }); } } }; + + const onSave = () => { + dashboardStore.saveMetric(metric, dashboardId); + } return useObserver(() => (
@@ -141,20 +146,30 @@ function WidgetForm(props: Props) {
- +
- - + {metric.widgetId && ( + <> + + + + )}
)); } -export default WidgetForm; \ No newline at end of file +export default withRouter(WidgetForm); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index 12ec3113b..1e39c2c63 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -1,7 +1,7 @@ import React from 'react'; import cn from 'classnames'; import WidgetWrapper from '../../WidgetWrapper'; -import { useDashboardStore } from '../../store/store'; +import { useStore } from 'App/mstore'; import { Loader, NoContent, SegmentSelection, Icon } from 'UI'; import DateRange from 'Shared/DateRange'; import { useObserver } from 'mobx-react-lite'; @@ -11,8 +11,8 @@ interface Props { } function WidgetPreview(props: Props) { const { className = '' } = props; - const store: any = useDashboardStore(); - const metric = store.currentWidget; + const { dashboardStore } = useStore(); + const metric: any = dashboardStore.currentWidget; const isTimeSeries = metric.metricType === 'timeseries'; const isTable = metric.metricType === 'table'; diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index 60eb1a569..cb39f7d3d 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { NoContent } from 'UI'; import cn from 'classnames'; -import { useDashboardStore } from '../../store/store'; +import { useStore } from 'App/mstore'; import SessionItem from 'Shared/SessionItem'; interface Props { @@ -9,8 +9,8 @@ interface Props { } function WidgetSessions(props: Props) { const { className = '' } = props; - const store: any = useDashboardStore(); - const widget = store.currentWidget; + const { dashboardStore } = useStore(); + const widget = dashboardStore.currentWidget; return (
diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index cbe2d38e4..4ad9eaa00 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { withRouter } from 'react-router-dom'; -import { useDashboardStore } from '../../store/store'; +import { useStore } from 'App/mstore'; import WidgetForm from '../WidgetForm'; import WidgetPreview from '../WidgetPreview'; import WidgetSessions from '../WidgetSessions'; @@ -11,8 +11,8 @@ interface Props { } function WidgetView(props: Props) { const [expanded, setExpanded] = useState(true); - const store: any = useDashboardStore(); - const widget = store.currentWidget; + const { dashboardStore } = useStore(); + const widget = dashboardStore.currentWidget; return (
diff --git a/frontend/app/components/Dashboard/store/dashboard.ts b/frontend/app/components/Dashboard/store/dashboard.ts index dbc6d221f..d2e4596d5 100644 --- a/frontend/app/components/Dashboard/store/dashboard.ts +++ b/frontend/app/components/Dashboard/store/dashboard.ts @@ -1,15 +1,39 @@ import { makeAutoObservable, observable, action, runInAction } from "mobx" -import Widget from "./widget" -// import APIClient from 'App/api_client'; +import Widget, { IWidget } from "./widget" +import { dashboardService } from 'App/services' -export default class Dashboard { +export interface IDashboard { + dashboardId: any + name: string + isPublic: boolean + widgets: IWidget[] + isValid: boolean + isPinned: boolean + currentWidget: IWidget + + update(data: any): void + toJson(): any + fromJson(json: any): void + validate(): void + addWidget(widget: IWidget): void + removeWidget(widgetId: string): void + updateWidget(widget: IWidget): void + getWidget(widgetId: string): void + getWidgetIndex(widgetId: string) + getWidgetByIndex(index: number): void + getWidgetCount(): void + getWidgetIndexByWidgetId(widgetId: string): void + swapWidgetPosition(positionA: number, positionB: number): void + sortWidgets(): void +} +export default class Dashboard implements IDashboard { dashboardId: any = undefined name: string = "New Dashboard" isPublic: boolean = false - widgets: Widget[] = [] + widgets: IWidget[] = [] isValid: boolean = false isPinned: boolean = false - currentWidget: Widget = new Widget() + currentWidget: IWidget = new Widget() constructor() { makeAutoObservable(this, { @@ -57,8 +81,8 @@ export default class Dashboard { runInAction(() => { this.dashboardId = json.dashboardId this.name = json.name - this.isPublic = json.isPrivate - this.widgets = json.widgets.map(w => new Widget().fromJson(w)) + this.isPublic = json.isPublic + this.widgets = json.widgets ? json.widgets.map(w => new Widget().fromJson(w)) : [] }) return this } @@ -68,7 +92,7 @@ export default class Dashboard { return this.isValid = this.name.length > 0 } - addWidget(widget: Widget) { + addWidget(widget: IWidget) { this.widgets.push(widget) } @@ -76,7 +100,7 @@ export default class Dashboard { this.widgets = this.widgets.filter(w => w.widgetId !== widgetId) } - updateWidget(widget: Widget) { + updateWidget(widget: IWidget) { const index = this.widgets.findIndex(w => w.widgetId === widget.widgetId) if (index >= 0) { this.widgets[index] = widget diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 8373c3fe9..8ca3f6c2e 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -1,20 +1,69 @@ import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" -import Dashboard from "./dashboard" +import Dashboard, { IDashboard } from "./dashboard" import APIClient from 'App/api_client'; -import Widget from "./widget"; -export default class DashboardStore { +import Widget, { IWidget } from "./widget"; +import { dashboardService } from "App/services"; + +export interface IDashboardSotre { + dashboards: IDashboard[] + widgetTemplates: any[] + selectedDashboard: IDashboard | null + dashboardInstance: IDashboard + siteId: any + currentWidget: Widget + widgetCategories: any[] + widgets: Widget[] + metricsPage: number + metricsPageSize: number + metricsSearch: string + + isLoading: boolean + isSaving: boolean + + initDashboard(dashboard?: IDashboard): void + updateKey(key: string, value: any): void + resetCurrentWidget(): void + editWidget(widget: any): void + fetchList(): void + fetch(dashboardId: string) + save(dashboard: IDashboard): Promise + saveDashboardWidget(dashboard: Dashboard, widget: Widget) + delete(dashboard: IDashboard) + toJson(): void + fromJson(json: any): void + initDashboard(dashboard: IDashboard): void + addDashboard(dashboard: IDashboard): void + removeDashboard(dashboard: IDashboard): void + getDashboard(dashboardId: string): void + getDashboardIndex(dashboardId: string): number + getDashboardCount(): void + getDashboardIndexByDashboardId(dashboardId: string): number + updateDashboard(dashboard: IDashboard): void + selectDashboardById(dashboardId: string): void + setSiteId(siteId: any): void + selectDefaultDashboard(): Promise + + saveMetric(metric: IWidget, dashboardId?: string): Promise +} +export default class DashboardStore implements IDashboardSotre { + siteId: any = null + // Dashbaord / Widgets dashboards: Dashboard[] = [] widgetTemplates: any[] = [] selectedDashboard: Dashboard | null = new Dashboard() - newDashboard: Dashboard = new Dashboard() - isLoading: boolean = false - siteId: any = null + dashboardInstance: IDashboard = new Dashboard() currentWidget: Widget = new Widget() widgetCategories: any[] = [] widgets: Widget[] = [] + + // Metrics metricsPage: number = 1 metricsPageSize: number = 10 metricsSearch: string = '' + + // Loading states + isLoading: boolean = false + isSaving: boolean = false private client = new APIClient() @@ -40,25 +89,15 @@ export default class DashboardStore { // TODO remove this sample data - this.dashboards = sampleDashboards - // this.selectedDashboard = sampleDashboards[0] + // this.dashboards = sampleDashboards - // setInterval(() => { - // this.selectedDashboard?.addWidget(getRandomWidget()) - // }, 3000) - - // setInterval(() => { - // this.selectedDashboard?.widgets[4].update({ position: 2 }) - // this.selectedDashboard?.swapWidgetPosition(2, 0) - // }, 3000) - - for (let i = 0; i < 15; i++) { - const widget: any= {}; - widget.widgetId = `${i}` - widget.name = `Widget ${i}`; - widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; - this.widgets.push(widget) - } + // for (let i = 0; i < 15; i++) { + // const widget: any= {}; + // widget.widgetId = `${i}` + // widget.name = `Widget ${i}`; + // widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; + // this.widgets.push(widget) + // } for (let i = 0; i < 4; i++) { const cat: any = { @@ -79,7 +118,10 @@ export default class DashboardStore { this.widgetCategories.push(cat) } - + } + + initDashboard(dashboard: Dashboard) { + this.dashboardInstance = dashboard || new Dashboard() } updateKey(key: any, value: any) { @@ -90,59 +132,71 @@ export default class DashboardStore { this.currentWidget = new Widget() } - editWidget(widget: Widget) { + editWidget(widget: any) { this.currentWidget.update(widget) } fetchList() { this.isLoading = true - this.client.get('/dashboards') - .then(response => { + dashboardService.getDashboards() + .then((list: any) => { + runInAction(() => { + this.dashboards = list.map(d => new Dashboard().fromJson(d)) + }) + }).finally(() => { runInAction(() => { - this.dashboards = response.data.map(d => new Dashboard().fromJson(d)) this.isLoading = false }) - } - ) + }) } fetch(dashboardId: string) { this.isLoading = true - this.client.get(`/dashboards/${dashboardId}`) - .then(response => { - runInAction(() => { - this.selectedDashboard = new Dashboard().fromJson(response.data) - this.isLoading = false - }) - } - ) + dashboardService.getDashboard(dashboardId).then(response => { + runInAction(() => { + this.selectedDashboard = new Dashboard().fromJson(response) + }) + }).finally(() => { + runInAction(() => { + this.isLoading = false + }) + }) } - save(dashboard: Dashboard) { - dashboard.validate() - if (dashboard.isValid) { - this.isLoading = true - if (dashboard.dashboardId) { - this.client.put(`/dashboards/${dashboard.dashboardId}`, dashboard.toJson()) - .then(response => { - runInAction(() => { - this.isLoading = false - }) - } - ) - } else { - this.client.post('/dashboards', dashboard.toJson()) - .then(response => { - runInAction(() => { - this.isLoading = false - }) - } - ) - } - } else { - alert("Invalid dashboard") // TODO show validation errors - } + save(dashboard: IDashboard): Promise { + this.isSaving = true + const isCreating = !dashboard.dashboardId + return dashboardService.saveDashboard(dashboard).then(response => { + runInAction(() => { + if (isCreating) { + this.addDashboard(response.data) + } else { + this.updateDashboard(response.data) + } + }) + }).finally(() => { + runInAction(() => { + this.isSaving = false + }) + }) + } + + saveMetric(metric: IWidget, dashboardId: string): Promise { + const isCreating = !metric.widgetId + return dashboardService.saveMetric(metric, dashboardId).then(metric => { + runInAction(() => { + if (isCreating) { + this.selectedDashboard?.widgets.push(metric) + } else { + this.selectedDashboard?.widgets.map(w => { + if (w.widgetId === metric.widgetId) { + w.update(metric) + } + }) + } + }) + }) } saveDashboardWidget(dashboard: Dashboard, widget: Widget) { @@ -193,10 +247,6 @@ export default class DashboardStore { return this } - initDashboard(dashboard: Dashboard | null) { - this.selectedDashboard = dashboard || new Dashboard() - } - addDashboard(dashboard: Dashboard) { this.dashboards.push(dashboard) } @@ -234,13 +284,16 @@ export default class DashboardStore { selectDashboardById = (dashboardId: any) => { this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || new Dashboard(); + if (this.selectedDashboard.dashboardId) { + this.fetch(this.selectedDashboard.dashboardId) + } } setSiteId = (siteId: any) => { this.siteId = siteId } - selectDefaultDashboard = () => { + selectDefaultDashboard = (): Promise => { return new Promise((resolve, reject) => { if (this.dashboards.length > 0) { const pinnedDashboard = this.dashboards.find(d => d.isPinned) @@ -250,7 +303,11 @@ export default class DashboardStore { this.selectedDashboard = this.dashboards[0] } } - resolve(this.selectedDashboard) + if (this.selectedDashboard) { + resolve(this.selectedDashboard) + } + + reject(new Error("No dashboards found")) }) } } diff --git a/frontend/app/components/Dashboard/store/widget.ts b/frontend/app/components/Dashboard/store/widget.ts index 5b0329cab..481e7c969 100644 --- a/frontend/app/components/Dashboard/store/widget.ts +++ b/frontend/app/components/Dashboard/store/widget.ts @@ -2,7 +2,36 @@ import { makeAutoObservable, runInAction, observable, action, reaction } from "m import Filter from 'Types/filter'; import FilterSeries from "./filterSeries"; -export default class Widget { +export interface IWidget { + widgetId: any + name: string + metricType: string + metricOf: string + metricValue: string + viewType: string + series: FilterSeries[] + sessions: [] + isPublic: boolean + owner: string + lastModified: Date + dashboardIds: any[] + + position: number + data: any + isLoading: boolean + isValid: boolean + dashboardId: any + colSpan: number + + udpateKey(key: string, value: any): void + removeSeries(index: number): void + addSeries(): void + fromJson(json: any): void + toJson(): any + validate(): void + update(data: any): void +} +export default class Widget implements IWidget { widgetId: any = undefined name: string = "New Metric" metricType: string = "timeseries" @@ -11,7 +40,7 @@ export default class Widget { viewType: string = "lineChart" series: FilterSeries[] = [] sessions: [] = [] - isPrivate: boolean = false + isPublic: boolean = false owner: string = "" lastModified: Date = new Date() dashboardIds: any[] = [] @@ -76,7 +105,12 @@ export default class Widget { return { widgetId: this.widgetId, name: this.name, - data: this.data + metricOf: this.metricOf, + metricValue: this.metricValue, + viewType: this.viewType, + series: this.series, + sessions: this.sessions, + isPublic: this.isPublic, } } diff --git a/frontend/app/components/Modal/Modal.js b/frontend/app/components/Modal/Modal.js index af0eff6b3..baf226621 100644 --- a/frontend/app/components/Modal/Modal.js +++ b/frontend/app/components/Modal/Modal.js @@ -1,16 +1,15 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import ReactDOM from 'react-dom'; +import { useModal } from '.'; +import ModalOverlay from './ModalOverlay'; -export default class Modal extends React.PureComponent { - constructor(props) { - super(props); - this.el = document.createElement('div'); - } +export default function Modal({ children }){ + const { component } = useModal(); - render() { - return ReactDOM.createPortal( - this.props.children, - this.el, - ); - } + return component ? ReactDOM.createPortal( + + {component} + , + document.querySelector("#modal-root"), + ) : null; } \ No newline at end of file diff --git a/frontend/app/components/Modal/ModalOverlay.css b/frontend/app/components/Modal/ModalOverlay.css new file mode 100644 index 000000000..e3e33562a --- /dev/null +++ b/frontend/app/components/Modal/ModalOverlay.css @@ -0,0 +1,32 @@ +.overlay { + /* absolute w-full h-screen cursor-pointer */ + position: absolute; + width: 100%; + height: 100vh; + cursor: pointer; + /* transition: all 0.3s ease-in-out; */ + animation: fade 1s forwards; +} +.slide { + position: absolute; + left: -100%; + -webkit-animation: slide 0.5s forwards; + animation: slide 0.5s forwards; +} + +@keyframes fade { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +@-webkit-keyframes slide { + 100% { left: 0; } +} + +@keyframes slide { + 100% { left: 0; } +} \ No newline at end of file diff --git a/frontend/app/components/Modal/ModalOverlay.tsx b/frontend/app/components/Modal/ModalOverlay.tsx index 003f29ee6..8660f53a4 100644 --- a/frontend/app/components/Modal/ModalOverlay.tsx +++ b/frontend/app/components/Modal/ModalOverlay.tsx @@ -1,14 +1,19 @@ import React from 'react'; import { ModalContext } from "App/components/Modal/modalContext"; -import useModal from 'App/components/Modal/useModal'; +import { useModal } from 'App/components/Modal'; +import stl from './ModalOverlay.css' function ModalOverlay({ children }) { let modal = useModal(); - // console.log('m', m); return ( -
modal.handleModal(false)} style={{ background: "rgba(0,0,0,0.8)", zIndex: '9999' }}> - {children} +
+
modal.hideModal()} + className={stl.overlay} + style={{ background: "rgba(0,0,0,0.5)" }} + /> +
{children}
); } diff --git a/frontend/app/components/Modal/index.tsx b/frontend/app/components/Modal/index.tsx new file mode 100644 index 000000000..f822178d3 --- /dev/null +++ b/frontend/app/components/Modal/index.tsx @@ -0,0 +1,44 @@ +import React, { Component, createContext } from 'react'; +import Modal from './Modal'; + +const ModalContext = createContext({ + component: null, + props: {}, + showModal: (component: any, props: any) => {}, + hideModal: () => {} +}); + +export class ModalProvider extends Component { + showModal = (component, props = {}) => { + this.setState({ + component, + props + }); + }; + + hideModal = () => + this.setState({ + component: null, + props: {} + }); + + state = { + component: null, + props: {}, + showModal: this.showModal, + hideModal: this.hideModal + }; + + render() { + return ( + + + {this.props.children} + + ); + } +} + +export const ModalConsumer = ModalContext.Consumer; + +export const useModal = () => React.useContext(ModalContext); \ No newline at end of file diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index 629ea33ad..df06dbbbe 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -5,28 +5,27 @@ import { Provider } from 'react-redux'; import store from './store'; import Router from './Router'; -import DashboardStore from './components/Dashboard/store'; -import { DashboardStoreProvider } from './components/Dashboard/store/store'; -import { ModalProvider } from './components/Modal/ModalContext'; +import { StoreProvider, RootStore } from './mstore'; +import { ModalProvider } from './components/Modal'; import ModalRoot from './components/Modal/ModalRoot'; import { HTML5Backend } from 'react-dnd-html5-backend' import { DndProvider } from 'react-dnd' +import Modal from 'react-modal'; - +Modal.setAppElement('#modal-root'); document.addEventListener('DOMContentLoaded', () => { - const dashboardStore = new DashboardStore(); render( ( - - + + - - + + - - + + ), document.getElementById('app'), diff --git a/frontend/app/mstore/index.tsx b/frontend/app/mstore/index.tsx new file mode 100644 index 000000000..ce928b399 --- /dev/null +++ b/frontend/app/mstore/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import DashboardStore, { IDashboardSotre } from 'App/components/Dashboard/store/DashboardStore'; + +export class RootStore { + dashboardStore: IDashboardSotre; + constructor() { + this.dashboardStore = new DashboardStore(); + } +} + +const StoreContext = React.createContext({} as RootStore); + +export const StoreProvider = ({ children, store }) => { + return ( + {children} + ); +}; + +export const useStore = () => React.useContext(StoreContext); + +export const withStore = (Component) => (props) => { + return ; +}; + diff --git a/frontend/app/services/DashboardService.ts b/frontend/app/services/DashboardService.ts new file mode 100644 index 000000000..871fabb3f --- /dev/null +++ b/frontend/app/services/DashboardService.ts @@ -0,0 +1,142 @@ +import { IDashboard } from "App/components/Dashboard/store/dashboard"; +import APIClient from 'App/api_client'; +import { IWidget } from "App/components/Dashboard/store/widget"; + +export interface IDashboardService { + initClient(): void + getWidgets(dashboardId: string): Promise + + getDashboards(): Promise + getDashboard(dashboardId: string): Promise + + saveDashboard(dashboard: IDashboard): Promise + deleteDashboard(dashboardId: string): Promise + + saveMetric(metric: IWidget, dashboardId?: string): Promise + deleteMetric(metricId: string): Promise + + saveWidget(dashboardId: string, widget: IWidget): Promise + deleteWidget(dashboardId: string, widgetId: string): Promise +} + + +export class DashboardService implements IDashboardService { + private client: APIClient; + + constructor(client?: APIClient) { + this.client = client ? client : new APIClient(); + } + + initClient() { + this.client = new APIClient(); + } + + /** + * Get all widgets from a dashboard. + * @param dashboardId Required + * @returns + */ + getWidgets(dashboardId: string): Promise { + return this.client.get(`/dashboards/${dashboardId}/widgets`) + .then(response => response.json()) + .then(response => response.data || []); + } + + + /** + * Get all dashboards. + * @returns {Promise} + */ + getDashboards(): Promise { + return this.client.get('/dashboards') + .then(response => response.json()) + .then(response => response.data || []); + } + + /** + * Get a dashboard by dashboardId. + * @param dashboardId + * @returns {Promise} + */ + getDashboard(dashboardId: string): Promise { + return this.client.get('/dashboards/' + dashboardId) + .then(response => response.json()) + .then(response => response.data || {}); + } + + /** + * Create or update a dashboard. + * @param dashboard Required + * @returns {Promise} + */ + saveDashboard(dashboard: IDashboard): Promise { + const data = dashboard.toJson(); + if (dashboard.dashboardId) { + return this.client.put(`/dashboards/${dashboard.dashboardId}`, data) + .then(response => response.json()) + .then(response => response.data || {}); + } else { + return this.client.post('/dashboards', data) + .then(response => response.json()) + .then(response => response.data || {}); + } + } + + /** + * Delete a dashboard. + * @param dashboardId + * @returns {Promise} + */ + deleteDashboard(dashboardId: string): Promise { + return this.client.delete(`/dashboards/${dashboardId}`) + } + + + /** + * Create a new Meitrc, if the dashboardId is not provided, + * it will add the metric to the dashboard. + * @param metric Required + * @param dashboardId Optional + * @returns {Promise} + */ + saveMetric(metric: IWidget, dashboardId?: string): Promise { + const data = metric.toJson(); + + const path = dashboardId ? `/metrics` : '/metrics'; // TODO change to /dashboards/:dashboardId/widgets + // const path = dashboardId ? `/dashboards/${dashboardId}/widgets` : '/widgets'; + if (metric.widgetId) { + return this.client.put(path + '/' + metric.widgetId, data) + } else { + return this.client.post(path, data) + } + } + + /** + * Delete a Metric by metricId. + * @param metricId + * @returns {Promise} + */ + deleteMetric(metricId: string): Promise { + return this.client.delete(`/metrics/${metricId}`) + } + + /** + * Remove a widget from a dashboard. + * @param dashboardId Required + * @param widgetId Required + * @returns {Promise} + */ + deleteWidget(dashboardId: string, widgetId: string): Promise { + return this.client.delete(`/dashboards/${dashboardId}/widgets/${widgetId}`) + } + + /** + * Add a widget to a dashboard. + * @param dashboardId Required + * @param widget Required + * @returns {Promise} + */ + saveWidget(dashboardId: string, widget: IWidget): Promise { + return this.client.post(`/dashboards/${dashboardId}/widgets`, widget.toJson()) + } +} \ No newline at end of file diff --git a/frontend/app/services/index.ts b/frontend/app/services/index.ts new file mode 100644 index 000000000..49ad0eb0c --- /dev/null +++ b/frontend/app/services/index.ts @@ -0,0 +1,3 @@ +import { DashboardService, IDashboardService } from "./DashboardService"; + +export const dashboardService: IDashboardService = new DashboardService(); From 17436657691391eddd6d9bd8f0b04674ce13d99a Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 30 Mar 2022 18:38:48 +0200 Subject: [PATCH 30/87] feat(api): upgrade JIRA feat(api): changed code to support new JIRA SDK feat(api): JIRA helper return error to UI feat(api): JIRA catch 401 --- api/chalicelib/utils/jira_client.py | 35 ++++++++++++++++------------- api/requirements.txt | 2 +- ee/api/requirements.txt | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/api/chalicelib/utils/jira_client.py b/api/chalicelib/utils/jira_client.py index d3b637373..78ed06bbc 100644 --- a/api/chalicelib/utils/jira_client.py +++ b/api/chalicelib/utils/jira_client.py @@ -5,19 +5,20 @@ import requests from jira import JIRA from jira.exceptions import JIRAError from requests.auth import HTTPBasicAuth +from starlette import status +from starlette.exceptions import HTTPException fields = "id, summary, description, creator, reporter, created, assignee, status, updated, comment, issuetype, labels" class JiraManager: - # retries = 5 retries = 0 def __init__(self, url, username, password, project_id=None): self._config = {"JIRA_PROJECT_ID": project_id, "JIRA_URL": url, "JIRA_USERNAME": username, "JIRA_PASSWORD": password} try: - self._jira = JIRA({'server': url}, basic_auth=(username, password), logging=True, max_retries=1) + self._jira = JIRA(url, basic_auth=(username, password), logging=True, max_retries=1) except Exception as e: print("!!! JIRA AUTH ERROR") print(e) @@ -34,7 +35,7 @@ class JiraManager: time.sleep(1) return self.get_projects() print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") projects_dict_list = [] for project in projects: projects_dict_list.append(self.__parser_project_info(project)) @@ -50,7 +51,7 @@ class JiraManager: time.sleep(1) return self.get_project() print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") return self.__parser_project_info(project) def get_issues(self, sql: str, offset: int = 0): @@ -66,7 +67,7 @@ class JiraManager: time.sleep(1) return self.get_issues(sql, offset) print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") issue_dict_list = [] for issue in issues: @@ -86,7 +87,7 @@ class JiraManager: time.sleep(1) return self.get_issue(issue_id) print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") return self.__parser_issue_info(issue) def get_issue_v3(self, issue_id: str): @@ -106,7 +107,7 @@ class JiraManager: time.sleep(1) return self.get_issue_v3(issue_id) print(f"=>Error {e}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") return self.__parser_issue_info(issue.json()) def create_issue(self, issue_dict): @@ -120,7 +121,7 @@ class JiraManager: time.sleep(1) return self.create_issue(issue_dict) print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") def close_issue(self, issue): try: @@ -132,7 +133,7 @@ class JiraManager: time.sleep(1) return self.close_issue(issue) print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") def assign_issue(self, issue_id, account_id) -> bool: try: @@ -143,7 +144,7 @@ class JiraManager: time.sleep(1) return self.assign_issue(issue_id, account_id) print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") def add_comment(self, issue_id: str, comment: str): try: @@ -154,7 +155,7 @@ class JiraManager: time.sleep(1) return self.add_comment(issue_id, comment) print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") return self.__parser_comment_info(comment) def add_comment_v3(self, issue_id: str, comment: str): @@ -191,7 +192,7 @@ class JiraManager: time.sleep(1) return self.add_comment_v3(issue_id, comment) print(f"=>Error {e}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") return self.__parser_comment_info(comment_response.json()) def get_comments(self, issueKey): @@ -207,7 +208,7 @@ class JiraManager: time.sleep(1) return self.get_comments(issueKey) print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") def get_meta(self): meta = {} @@ -217,14 +218,16 @@ class JiraManager: def get_assignable_users(self): try: - users = self._jira.search_assignable_users_for_issues('', project=self._config['JIRA_PROJECT_ID']) + users = self._jira.search_assignable_users_for_issues(project=self._config['JIRA_PROJECT_ID'], query="*") except JIRAError as e: self.retries -= 1 if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.get_assignable_users() print(f"=>Error {e.text}") - raise e + if e.status_code == 401: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="JIRA: 401 Unauthorized") + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") users_dict = [] for user in users: users_dict.append({ @@ -245,7 +248,7 @@ class JiraManager: time.sleep(1) return self.get_issue_types() print(f"=>Error {e.text}") - raise e + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") types_dict = [] for type in types: if not type.subtask and not type.name.lower() == "epic": diff --git a/api/requirements.txt b/api/requirements.txt index e5672d80a..198b535dd 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -4,7 +4,7 @@ boto3==1.16.1 pyjwt==1.7.1 psycopg2-binary==2.8.6 elasticsearch==7.9.1 -jira==2.0.0 +jira==3.1.1 diff --git a/ee/api/requirements.txt b/ee/api/requirements.txt index 84a372567..ef143038b 100644 --- a/ee/api/requirements.txt +++ b/ee/api/requirements.txt @@ -4,7 +4,7 @@ boto3==1.16.1 pyjwt==1.7.1 psycopg2-binary==2.8.6 elasticsearch==7.9.1 -jira==2.0.0 +jira==3.1.1 clickhouse-driver==0.2.2 python3-saml==1.12.0 From 7d18c093ebf6479ce7aed626160ea93d6c6cd715 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 30 Mar 2022 20:00:58 +0200 Subject: [PATCH 31/87] feat(api): fixed JIRA error handling feat(api): fixed dashboard split old metrics --- api/chalicelib/core/dashboard.py | 22 ++++++++++----------- api/chalicelib/utils/jira_client.py | 30 ++++++++++++++--------------- api/routers/subs/dashboard.py | 12 ++++++++---- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index bab19ba12..6de949047 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2261,14 +2261,15 @@ def get_application_activity_avg_image_load_time(project_id, startTimestamp=Time endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: row = __get_application_activity_avg_image_load_time(cur, project_id, startTimestamp, endTimestamp, **args) - results = helper.dict_to_camel_case(row) + results = row + results["chart"] = get_performance_avg_image_load_time(project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff row = __get_application_activity_avg_image_load_time(cur, project_id, startTimestamp, endTimestamp, **args) previous = helper.dict_to_camel_case(row) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) - results["chart"] = get_performance_avg_image_load_time(project_id, startTimestamp, endTimestamp, **args) + return results @@ -2297,8 +2298,7 @@ def get_performance_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(d AND resources.type = 'img' AND resources.duration>0 {(f' AND ({" OR ".join(img_constraints)})') if len(img_constraints) > 0 else ""} ) - SELECT - generated_timestamp AS timestamp, + SELECT generated_timestamp AS timestamp, COALESCE(AVG(resources.duration),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( @@ -2337,14 +2337,14 @@ def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeU endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: row = __get_application_activity_avg_page_load_time(cur, project_id, startTimestamp, endTimestamp, **args) - results = helper.dict_to_camel_case(row) + results = row + results["chart"] = get_performance_avg_page_load_time(project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff row = __get_application_activity_avg_page_load_time(cur, project_id, startTimestamp, endTimestamp, **args) previous = helper.dict_to_camel_case(row) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) - results["chart"] = get_performance_avg_page_load_time(project_id, startTimestamp, endTimestamp, **args) return results @@ -2369,8 +2369,7 @@ def get_performance_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(de WHERE {" AND ".join(pg_sub_query_subset)} AND pages.load_time>0 AND pages.load_time IS NOT NULL {(f' AND ({" OR ".join(location_constraints)})') if len(location_constraints) > 0 else ""} ) - SELECT - generated_timestamp AS timestamp, + SELECT generated_timestamp AS timestamp, COALESCE(AVG(pages.load_time),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT pages.load_time @@ -2407,14 +2406,14 @@ def get_application_activity_avg_request_load_time(project_id, startTimestamp=Ti endTimestamp=TimeUTC.now(), **args): with pg_client.PostgresClient() as cur: row = __get_application_activity_avg_request_load_time(cur, project_id, startTimestamp, endTimestamp, **args) - results = helper.dict_to_camel_case(row) + results = row + results["chart"] = get_performance_avg_request_load_time(project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff row = __get_application_activity_avg_request_load_time(cur, project_id, startTimestamp, endTimestamp, **args) previous = helper.dict_to_camel_case(row) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) - results["chart"] = get_performance_avg_request_load_time(project_id, startTimestamp, endTimestamp, **args) return results @@ -2447,8 +2446,7 @@ def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now AND resources.type = 'fetch' AND resources.duration>0 {(f' AND ({" OR ".join(request_constraints)})') if len(request_constraints) > 0 else ""} ) - SELECT - generated_timestamp AS timestamp, + SELECT generated_timestamp AS timestamp, COALESCE(AVG(resources.duration),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( diff --git a/api/chalicelib/utils/jira_client.py b/api/chalicelib/utils/jira_client.py index 78ed06bbc..8370feb5e 100644 --- a/api/chalicelib/utils/jira_client.py +++ b/api/chalicelib/utils/jira_client.py @@ -34,7 +34,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.get_projects() - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") projects_dict_list = [] for project in projects: @@ -50,7 +50,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.get_project() - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") return self.__parser_project_info(project) @@ -66,7 +66,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.get_issues(sql, offset) - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") issue_dict_list = [] @@ -86,7 +86,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.get_issue(issue_id) - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") return self.__parser_issue_info(issue) @@ -106,8 +106,8 @@ class JiraManager: if self.retries > 0: time.sleep(1) return self.get_issue_v3(issue_id) - print(f"=>Error {e}") - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") + print(f"=>Exception {e}") + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: get issue error") return self.__parser_issue_info(issue.json()) def create_issue(self, issue_dict): @@ -120,7 +120,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.create_issue(issue_dict) - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") def close_issue(self, issue): @@ -132,7 +132,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.close_issue(issue) - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") def assign_issue(self, issue_id, account_id) -> bool: @@ -143,7 +143,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.assign_issue(issue_id, account_id) - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") def add_comment(self, issue_id: str, comment: str): @@ -154,7 +154,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.add_comment(issue_id, comment) - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") return self.__parser_comment_info(comment) @@ -191,8 +191,8 @@ class JiraManager: if self.retries > 0: time.sleep(1) return self.add_comment_v3(issue_id, comment) - print(f"=>Error {e}") - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") + print(f"=>Exception {e}") + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: comment error") return self.__parser_comment_info(comment_response.json()) def get_comments(self, issueKey): @@ -207,7 +207,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.get_comments(issueKey) - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") def get_meta(self): @@ -224,7 +224,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.get_assignable_users() - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") if e.status_code == 401: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="JIRA: 401 Unauthorized") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") @@ -247,7 +247,7 @@ class JiraManager: if (e.status_code // 100) == 4 and self.retries > 0: time.sleep(1) return self.get_issue_types() - print(f"=>Error {e.text}") + print(f"=>Exception {e.text}") raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}") types_dict = [] for type in types: diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index e81e18b5e..fdb5d641a 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -325,7 +325,7 @@ def get_dashboard_resources_count_by_type(projectId: int, data: schemas.MetricPa @app.post('/{projectId}/dashboard/overview', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/overview', tags=["dashboard", "metrics"]) def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": [ + results = [ {"key": "count_sessions", "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, *helper.explode_widget(data={**dashboard.get_application_activity(project_id=projectId, **data.dict()), @@ -343,13 +343,15 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body {"key": "avg_used_js_heap_size", "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, {"key": "avg_cpu", "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, {"key": schemas.TemplateKeys.avg_fps, "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} - ]} + ] + results = sorted(results, key=lambda r: r["key"]) + return {"data": results} @app.post('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"]) def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": [ + results = [ {"key": schemas.TemplateKeys.count_sessions, "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, {"key": schemas.TemplateKeys.avg_image_load_time, @@ -388,4 +390,6 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, {"key": schemas.TemplateKeys.avg_cpu, "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, {"key": schemas.TemplateKeys.avg_fps, "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} - ]} + ] + results = sorted(results, key=lambda r: r["key"]) + return {"data": results} From af1c88a573424926bd37d9d769ad866dd43215ce Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 30 Mar 2022 20:29:22 +0200 Subject: [PATCH 32/87] feat(api): create dashboard with widgets at the same time --- api/chalicelib/core/dashboards2.py | 17 +++++++++++++---- api/routers/subs/metrics.py | 4 ++-- api/schemas.py | 18 ++++++++++-------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 215a4f9ea..adb7884fe 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -27,11 +27,20 @@ def create_dashboard(project_id, user_id, data: schemas.CreateDashboardSchema): with pg_client.PostgresClient() as cur: pg_query = f"""INSERT INTO dashboards(project_id, user_id, name, is_public, is_pinned) VALUES(%(projectId)s, %(userId)s, %(name)s, %(is_public)s, %(is_pinned)s) - RETURNING *;""" + RETURNING *""" params = {"userId": user_id, "projectId": project_id, **data.dict()} + if data.metrics is not None and len(data.metrics) > 0: + pg_query = f"""WITH dash AS ({pg_query}) + INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id) + VALUES {",".join([f"((SELECT dashboard_id FROM dash),%(metric_id_{i})s, %(userId)s)" for i in range(len(data.metrics))])} + RETURNING (SELECT dashboard_id FROM dash)""" + for i, m in enumerate(data.metrics): + params[f"metric_id_{i}"] = m cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() - return helper.dict_to_camel_case(row) + if row is None: + return {"errors": ["something went wrong while creating the dashboard"]} + return {"data": get_dashboard(project_id=project_id, user_id=user_id, dashboard_id=row["dashboard_id"])} def get_dashboards(project_id, user_id): @@ -87,10 +96,10 @@ def delete_dashboard(project_id, user_id, dashboard_id): return {"data": {"success": True}} -def update_dashboard(project_id, user_id, dashboard_id, data: schemas.CreateDashboardSchema): +def update_dashboard(project_id, user_id, dashboard_id, data: schemas.EditDashboardSchema): with pg_client.PostgresClient() as cur: pg_query = """UPDATE dashboards - SET name = %(name)s, is_pinned = %(is_pinned)s, is_public = %(is_public)s + SET name = %(name)s, is_public = %(is_public)s WHERE dashboards.project_id = %(projectId)s AND dashboard_id = %(dashboard_id)s AND (dashboards.user_id = %(userId)s OR is_public) diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 2d3c116d3..88cfd3937 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -12,7 +12,7 @@ public_app, app, app_apikey = get_routers() @app.put('/{projectId}/dashboards', tags=["dashboard"]) def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): - return {"data": dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)} + return dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data) @app.get('/{projectId}/dashboards', tags=["dashboard"]) @@ -27,7 +27,7 @@ def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentCont @app.post('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) @app.put('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) -def update_dashboard(projectId: int, dashboardId: int, data: schemas.CreateDashboardSchema = Body(...), +def update_dashboard(projectId: int, dashboardId: int, data: schemas.EditDashboardSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.update_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} diff --git a/api/schemas.py b/api/schemas.py index ef1972d98..0135aafc0 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -881,23 +881,25 @@ class CreateDashboardSchema(BaseModel): name: str = Field(..., min_length=1) is_public: bool = Field(default=False) is_pinned: bool = Field(default=False) + metrics: Optional[List[int]] = Field(default=[]) + + class Config: + alias_generator = attribute_to_camel_case + + +class EditDashboardSchema(BaseModel): + name: str = Field(..., min_length=1) + is_public: bool = Field(default=False) class Config: alias_generator = attribute_to_camel_case class AddWidgetToDashboardPayloadSchema(BaseModel): - metric_id: Optional[int] = Field(default=None) + metric_id: int = Field(default=None) name: Optional[str] = Field(default=None) config: dict = Field(default={}) - @root_validator - def validator(cls, values): - assert bool(values.get("template_id") is not None) != bool(values.get("metric_id") is not None), \ - f"templateId or metricId should be provided, but not both at the same time" - - return values - class Config: alias_generator = attribute_to_camel_case From e909321040c0787c601df1adec724a5f80aead0e Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 30 Mar 2022 20:38:30 +0200 Subject: [PATCH 33/87] feat(api): create new metric and add it to dashboard --- api/chalicelib/core/custom_metrics.py | 4 +++- api/chalicelib/core/dashboards2.py | 7 +++++++ api/routers/subs/metrics.py | 6 ++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index e0b0ed432..88f8fca10 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -102,7 +102,7 @@ def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessi return results -def create(project_id, user_id, data: schemas.CreateCustomMetricsSchema): +def create(project_id, user_id, data: schemas.CreateCustomMetricsSchema, dashboard=False): with pg_client.PostgresClient() as cur: _data = {} for i, s in enumerate(data.series): @@ -129,6 +129,8 @@ def create(project_id, user_id, data: schemas.CreateCustomMetricsSchema): query ) r = cur.fetchone() + if dashboard: + return r["metric_id"] return {"data": get(metric_id=r["metric_id"], project_id=project_id, user_id=user_id)} diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index adb7884fe..c292ed1a0 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -1,6 +1,7 @@ import json import schemas +from chalicelib.core import custom_metrics from chalicelib.utils import helper from chalicelib.utils import pg_client @@ -162,3 +163,9 @@ def pin_dashboard(project_id, user_id, dashboard_id): cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() return helper.dict_to_camel_case(row) + + +def create_metric_add_widget(project_id, user_id, dashboard_id, data: schemas.CreateCustomMetricsSchema): + metric_id = custom_metrics.create(project_id=project_id, user_id=user_id, data=data, dashboard=True) + return add_widget(project_id=project_id, user_id=user_id, dashboard_id=dashboard_id, + data=schemas.AddWidgetToDashboardPayloadSchema(metric_id=metric_id)) diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 88cfd3937..bcec3d167 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -51,6 +51,12 @@ def add_widget_to_dashboard(projectId: int, dashboardId: int, return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} +@app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"]) +@app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"]) +def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int, data: schemas.CreateCustomMetricsSchema = Body(...),context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.create_metric_add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + data=data)} + @app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) @app.put('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) From b799704efd051be7eb6e6edbc8a5607a077b70ea Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 30 Mar 2022 20:39:06 +0200 Subject: [PATCH 34/87] feat(api): dashboard format --- api/routers/subs/metrics.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index bcec3d167..4504ba8f1 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -51,11 +51,15 @@ def add_widget_to_dashboard(projectId: int, dashboardId: int, return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} + @app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"]) @app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"]) -def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int, data: schemas.CreateCustomMetricsSchema = Body(...),context: schemas.CurrentContext = Depends(OR_context)): - return {"data": dashboards2.create_metric_add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, - data=data)} +def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int, + data: schemas.CreateCustomMetricsSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.create_metric_add_widget(project_id=projectId, user_id=context.user_id, + dashboard_id=dashboardId, + data=data)} @app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) From cfcff0293aeca227cded754ae3a18ffd2528dd79 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 1 Apr 2022 15:26:24 +0200 Subject: [PATCH 35/87] feat(api): return attached dashboards with the custom metrics list feat(api): allow custom metrics visibility --- api/chalicelib/core/custom_metrics.py | 42 +++++++++++++++++++-------- api/schemas.py | 2 +- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 88f8fca10..6492080f8 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -205,29 +205,40 @@ def update(metric_id, user_id, project_id, data: schemas.UpdateCustomMetricsSche return get(metric_id=metric_id, project_id=project_id, user_id=user_id) -def get_all(project_id, user_id): +def get_all(project_id, user_id, include_series=False): with pg_client.PostgresClient() as cur: - cur.execute( - cur.mogrify( - """SELECT * - FROM metrics - LEFT JOIN LATERAL (SELECT jsonb_agg(metric_series.* ORDER BY index) AS series + sub_join = "" + if include_series: + sub_join = """LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index),'[]'::jsonb) AS series FROM metric_series WHERE metric_series.metric_id = metrics.metric_id AND metric_series.deleted_at ISNULL - ) AS metric_series ON (TRUE) + ) AS metric_series ON (TRUE)""" + cur.execute( + cur.mogrify( + f"""SELECT * + FROM metrics + {sub_join} + LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards + FROM (SELECT dashboard_id, name, is_public + FROM dashboards + WHERE deleted_at ISNULL + AND project_id = %(project_id)s + AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards + ) AS connected_dashboards ON (TRUE) WHERE metrics.project_id = %(project_id)s AND metrics.deleted_at ISNULL - AND (user_id = %(user_id)s OR is_public) + AND (user_id = %(user_id)s OR metrics.is_public) ORDER BY created_at;""", {"project_id": project_id, "user_id": user_id} ) ) rows = cur.fetchall() - for r in rows: - r["created_at"] = TimeUTC.datetime_to_timestamp(r["created_at"]) - for s in r["series"]: - s["filter"] = helper.old_search_payload_to_flat(s["filter"]) + if include_series: + for r in rows: + # r["created_at"] = TimeUTC.datetime_to_timestamp(r["created_at"]) + for s in r["series"]: + s["filter"] = helper.old_search_payload_to_flat(s["filter"]) rows = helper.list_to_camel_case(rows) return rows @@ -258,6 +269,13 @@ def get(metric_id, project_id, user_id, flatten=True): WHERE metric_series.metric_id = metrics.metric_id AND metric_series.deleted_at ISNULL ) AS metric_series ON (TRUE) + LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards + FROM (SELECT dashboard_id, name, is_public + FROM dashboards + WHERE deleted_at ISNULL + AND project_id = %(project_id)s + AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards + ) AS connected_dashboards ON (TRUE) WHERE metrics.project_id = %(project_id)s AND metrics.deleted_at ISNULL AND (metrics.user_id = %(user_id)s OR metrics.is_public) diff --git a/api/schemas.py b/api/schemas.py index 0135aafc0..821326728 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -820,7 +820,7 @@ class CustomMetricChartPayloadSchema(CustomMetricSessionsPayloadSchema): class CreateCustomMetricsSchema(CustomMetricChartPayloadSchema): name: str = Field(...) series: List[CustomMetricCreateSeriesSchema] = Field(..., min_items=1) - is_public: bool = Field(default=True, const=True) + is_public: bool = Field(default=True) view_type: Union[MetricTimeseriesViewType, MetricTableViewType] = Field(MetricTimeseriesViewType.line_chart) metric_type: MetricType = Field(MetricType.timeseries) metric_of: Union[TableMetricOfType, TimeseriesMetricOfType] = Field(TableMetricOfType.user_id) From 1807174b8be9b1755588e954f72119798ada9125 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 1 Apr 2022 20:29:13 +0200 Subject: [PATCH 36/87] feat(api): dashboard get predefined metrics charts feat(api): support root_path for Uvicorn server --- api/app.py | 2 +- api/chalicelib/core/custom_metrics.py | 51 +++++++++++++---- api/chalicelib/core/dashboards2.py | 79 ++++++++++++++++++++++++++- api/routers/subs/metrics.py | 20 ++++++- api/schemas.py | 10 ++++ 5 files changed, 144 insertions(+), 18 deletions(-) diff --git a/api/app.py b/api/app.py index ae7f051bf..959f1ef8f 100644 --- a/api/app.py +++ b/api/app.py @@ -13,7 +13,7 @@ from routers.crons import core_crons from routers.crons import core_dynamic_crons from routers.subs import dashboard, insights, metrics, v1_api -app = FastAPI() +app = FastAPI(root_path="/api") @app.middleware('http') diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 6492080f8..839ba4d72 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -54,13 +54,9 @@ def merged_live(project_id, data: schemas.CreateCustomMetricsSchema): return results -def __get_merged_metric(project_id, user_id, metric_id, - data: Union[schemas.CustomMetricChartPayloadSchema, - schemas.CustomMetricSessionsPayloadSchema]) \ +def __merge_metric_with_data(metric, data: Union[schemas.CustomMetricChartPayloadSchema, + schemas.CustomMetricSessionsPayloadSchema]) \ -> Union[schemas.CreateCustomMetricsSchema, None]: - metric = get(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False) - if metric is None: - return None metric: schemas.CreateCustomMetricsSchema = schemas.CreateCustomMetricsSchema.parse_obj({**data.dict(), **metric}) if len(data.filters) > 0 or len(data.events) > 0: for s in metric.series: @@ -71,11 +67,12 @@ def __get_merged_metric(project_id, user_id, metric_id, return metric -def make_chart(project_id, user_id, metric_id, data: schemas.CustomMetricChartPayloadSchema): - metric: schemas.CreateCustomMetricsSchema = __get_merged_metric(project_id=project_id, user_id=user_id, - metric_id=metric_id, data=data) +def make_chart(project_id, user_id, metric_id, data: schemas.CustomMetricChartPayloadSchema, metric=None): + if metric is None: + metric = get(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False) if metric is None: return None + metric: schemas.CreateCustomMetricsSchema = __merge_metric_with_data(metric=metric, data=data) series_charts = __try_live(project_id=project_id, data=metric) if metric.view_type == schemas.MetricTimeseriesViewType.progress or metric.metric_type == schemas.MetricType.table: return series_charts @@ -88,8 +85,10 @@ def make_chart(project_id, user_id, metric_id, data: schemas.CustomMetricChartPa def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessionsPayloadSchema): - metric: schemas.CreateCustomMetricsSchema = __get_merged_metric(project_id=project_id, user_id=user_id, - metric_id=metric_id, data=data) + metric = get(metric_id=metric_id, project_id=project_id, user_id=user_id, flatten=False) + if metric is None: + return None + metric: schemas.CreateCustomMetricsSchema = __merge_metric_with_data(metric=metric, data=data) if metric is None: return None results = [] @@ -294,6 +293,36 @@ def get(metric_id, project_id, user_id, flatten=True): return helper.dict_to_camel_case(row) +def get_with_template(metric_id, project_id, user_id): + with pg_client.PostgresClient() as cur: + cur.execute( + cur.mogrify( + """SELECT * + FROM metrics + LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index),'[]'::jsonb) AS series + FROM metric_series + WHERE metric_series.metric_id = metrics.metric_id + AND metric_series.deleted_at ISNULL + ) AS metric_series ON (TRUE) + LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards + FROM (SELECT dashboard_id, name, is_public + FROM dashboards + WHERE deleted_at ISNULL + AND project_id = %(project_id)s + AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards + ) AS connected_dashboards ON (TRUE) + WHERE (metrics.project_id = %(project_id)s OR metrics.project_id ISNULL) + AND metrics.deleted_at ISNULL + AND (metrics.user_id = %(user_id)s OR metrics.is_public) + AND metrics.metric_id = %(metric_id)s + ORDER BY created_at;""", + {"metric_id": metric_id, "project_id": project_id, "user_id": user_id} + ) + ) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + def get_series_for_alert(project_id, user_id): with pg_client.PostgresClient() as cur: cur.execute( diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index c292ed1a0..7e06f52ac 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -1,7 +1,7 @@ import json import schemas -from chalicelib.core import custom_metrics +from chalicelib.core import custom_metrics, dashboard from chalicelib.utils import helper from chalicelib.utils import pg_client @@ -79,7 +79,6 @@ def get_dashboard(project_id, user_id, dashboard_id): AND dashboard_id = %(dashboard_id)s AND (dashboards.user_id = %(userId)s OR is_public);""" params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id} - print(cur.mogrify(pg_query, params)) cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() return helper.dict_to_camel_case(row) @@ -111,6 +110,30 @@ def update_dashboard(project_id, user_id, dashboard_id, data: schemas.EditDashbo return helper.dict_to_camel_case(row) +def get_widget(project_id, user_id, dashboard_id, widget_id): + with pg_client.PostgresClient() as cur: + pg_query = """SELECT metrics.*, metric_series.series + FROM dashboard_widgets + INNER JOIN dashboards USING (dashboard_id) + INNER JOIN metrics USING (metric_id) + LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index), '[]'::jsonb) AS series + FROM metric_series + WHERE metric_series.metric_id = metrics.metric_id + AND metric_series.deleted_at ISNULL + ) AS metric_series ON (TRUE) + WHERE dashboard_id = %(dashboard_id)s + AND widget_id = %(widget_id)s + AND (dashboards.is_public OR dashboards.user_id = %(userId)s) + AND dashboards.deleted_at IS NULL + AND metrics.deleted_at ISNULL + AND (metrics.project_id = %(projectId)s OR metrics.project_id ISNULL) + AND (metrics.is_public OR metrics.user_id = %(userId)s);""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, "widget_id": widget_id} + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + + def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashboardPayloadSchema): with pg_client.PostgresClient() as cur: pg_query = """INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config, name) @@ -154,7 +177,7 @@ def pin_dashboard(project_id, user_id, dashboard_id): with pg_client.PostgresClient() as cur: pg_query = """UPDATE dashboards SET is_pinned = FALSE - WHERE dashboard_id=%(dashboard_id)s AND project_id=%(project_id)s; + WHERE project_id=%(project_id)s; UPDATE dashboards SET is_pinned = True WHERE dashboard_id=%(dashboard_id)s AND project_id=%(project_id)s AND deleted_at ISNULL @@ -169,3 +192,53 @@ def create_metric_add_widget(project_id, user_id, dashboard_id, data: schemas.Cr metric_id = custom_metrics.create(project_id=project_id, user_id=user_id, data=data, dashboard=True) return add_widget(project_id=project_id, user_id=user_id, dashboard_id=dashboard_id, data=schemas.AddWidgetToDashboardPayloadSchema(metric_id=metric_id)) + + +PREDEFINED = {schemas.TemplateKeys.count_sessions: dashboard.get_processed_sessions, + schemas.TemplateKeys.avg_image_load_time: dashboard.get_application_activity_avg_image_load_time, + schemas.TemplateKeys.avg_page_load_time: dashboard.get_application_activity_avg_page_load_time, + schemas.TemplateKeys.avg_request_load_time: dashboard.get_application_activity_avg_request_load_time, + schemas.TemplateKeys.avg_dom_content_load_start: dashboard.get_page_metrics_avg_dom_content_load_start, + schemas.TemplateKeys.avg_first_contentful_pixel: dashboard.get_page_metrics_avg_first_contentful_pixel, + schemas.TemplateKeys.avg_visited_pages: dashboard.get_user_activity_avg_visited_pages, + schemas.TemplateKeys.avg_session_duration: dashboard.get_user_activity_avg_session_duration, + schemas.TemplateKeys.avg_pages_dom_buildtime: dashboard.get_pages_dom_build_time, + schemas.TemplateKeys.avg_pages_response_time: dashboard.get_pages_response_time, + schemas.TemplateKeys.avg_response_time: dashboard.get_top_metrics_avg_response_time, + schemas.TemplateKeys.avg_first_paint: dashboard.get_top_metrics_avg_first_paint, + schemas.TemplateKeys.avg_dom_content_loaded: dashboard.get_top_metrics_avg_dom_content_loaded, + schemas.TemplateKeys.avg_till_first_bit: dashboard.get_top_metrics_avg_till_first_bit, + schemas.TemplateKeys.avg_time_to_interactive: dashboard.get_top_metrics_avg_time_to_interactive, + schemas.TemplateKeys.count_requests: dashboard.get_top_metrics_count_requests, + schemas.TemplateKeys.avg_time_to_render: dashboard.get_time_to_render, + schemas.TemplateKeys.avg_used_js_heap_size: dashboard.get_memory_consumption, + schemas.TemplateKeys.avg_cpu: dashboard.get_avg_cpu, + schemas.TemplateKeys.avg_fps: dashboard.get_avg_fps} + + +def get_predefined_metric(key: schemas.TemplateKeys, project_id: int, data: dict): + return PREDEFINED.get(key, lambda *args: None)(project_id=project_id, **data) + + +def make_chart_metrics(project_id, user_id, metric_id, data: schemas.CustomMetricChartPayloadSchema): + raw_metric = custom_metrics.get_with_template(metric_id=metric_id, project_id=project_id, user_id=user_id) + if raw_metric is None: + return None + metric = schemas.CustomMetricAndTemplate = schemas.CustomMetricAndTemplate.parse_obj(raw_metric) + if metric.is_template: + return get_predefined_metric(key=metric.key, project_id=project_id, data=data.dict()) + else: + return custom_metrics.make_chart(project_id=project_id, user_id=user_id, metric_id=metric_id, data=data, + metric=raw_metric) + + +def make_chart_widget(dashboard_id, project_id, user_id, widget_id, data: schemas.CustomMetricChartPayloadSchema): + raw_metric = get_widget(widget_id=widget_id, project_id=project_id, user_id=user_id, dashboard_id=dashboard_id) + if raw_metric is None: + return None + metric = schemas.CustomMetricAndTemplate = schemas.CustomMetricAndTemplate.parse_obj(raw_metric) + if metric.is_template: + return get_predefined_metric(key=metric.key, project_id=project_id, data=data.dict()) + else: + return custom_metrics.make_chart(project_id=project_id, user_id=user_id, metric_id=raw_metric["metricId"], + data=data, metric=raw_metric) diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 4504ba8f1..fc875e0fd 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -22,7 +22,10 @@ def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_ @app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} + data = dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId) + if data is None: + return {"errors": ["dashboard not found"]} + return {"data": data} @app.post('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) @@ -78,6 +81,17 @@ def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int widget_id=widgetId) +@app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}/chart', tags=["dashboard"]) +def get_widget_chart(projectId: int, dashboardId: int, widgetId: int, + data: schemas.CustomMetricChartPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + data = dashboards2.make_chart_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + widget_id=widgetId, data=data) + if data is None: + return {"errors": ["widget not found"]} + return {"data": data} + + @app.get('/{projectId}/metrics/templates', tags=["dashboard"]) def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.get_templates(project_id=projectId, user_id=context.user_id)} @@ -131,8 +145,8 @@ def get_custom_metric_sessions(projectId: int, metric_id: int, @app.post('/{projectId}/custom_metrics/{metric_id}/chart', tags=["customMetrics"]) def get_custom_metric_chart(projectId: int, metric_id: int, data: schemas.CustomMetricChartPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): - data = custom_metrics.make_chart(project_id=projectId, user_id=context.user_id, metric_id=metric_id, - data=data) + data = dashboards2.make_chart_metrics(project_id=projectId, user_id=context.user_id, metric_id=metric_id, + data=data) if data is None: return {"errors": ["custom metric not found"]} return {"data": data} diff --git a/api/schemas.py b/api/schemas.py index 821326728..b707a53e1 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -926,3 +926,13 @@ class TemplateKeys(str, Enum): avg_used_js_heap_size = "avg_used_js_heap_size" avg_cpu = "avg_cpu" avg_fps = "avg_fps" + + +# class CustomMetricAndTemplate(CreateCustomMetricsSchema): +class CustomMetricAndTemplate(BaseModel): + is_template: bool = Field(...) + project_id: Optional[int] = Field(...) + key: Optional[TemplateKeys] = Field(...) + + class Config: + alias_generator = attribute_to_camel_case From 642618046ad44e8c65ef067ed293c9f1e4faf464 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 4 Apr 2022 10:40:44 +0200 Subject: [PATCH 37/87] feat(api): get live session by session_id --- api/chalicelib/core/sessions.py | 9 +++++---- api/routers/core.py | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/api/chalicelib/core/sessions.py b/api/chalicelib/core/sessions.py index 1903cc08b..f2b34dc37 100644 --- a/api/chalicelib/core/sessions.py +++ b/api/chalicelib/core/sessions.py @@ -39,7 +39,8 @@ def __group_metadata(session, project_metadata): return meta -def get_by_id2_pg(project_id, session_id, user_id, full_data=False, include_fav_viewed=False, group_metadata=False): +def get_by_id2_pg(project_id, session_id, user_id, full_data=False, include_fav_viewed=False, group_metadata=False, + live=True): with pg_client.PostgresClient() as cur: extra_query = [] if include_fav_viewed: @@ -97,9 +98,9 @@ def get_by_id2_pg(project_id, session_id, user_id, full_data=False, include_fav_ data['metadata'] = __group_metadata(project_metadata=data.pop("projectMetadata"), session=data) data['issues'] = issues.get_by_session_id(session_id=session_id) - data['live'] = assist.is_live(project_id=project_id, - session_id=session_id, - project_key=data["projectKey"]) + data['live'] = live and assist.is_live(project_id=project_id, + session_id=session_id, + project_key=data["projectKey"]) data["inDB"] = True return data else: diff --git a/api/routers/core.py b/api/routers/core.py index 53fdba667..20864288d 100644 --- a/api/routers/core.py +++ b/api/routers/core.py @@ -21,6 +21,7 @@ from routers.base import get_routers public_app, app, app_apikey = get_routers() +@app.get('/{projectId}/sessions/{sessionId}', tags=["sessions"]) @app.get('/{projectId}/sessions2/{sessionId}', tags=["sessions"]) def get_session2(projectId: int, sessionId: Union[int, str], context: schemas.CurrentContext = Depends(OR_context)): if isinstance(sessionId, str): @@ -36,6 +37,7 @@ def get_session2(projectId: int, sessionId: Union[int, str], context: schemas.Cu } +@app.get('/{projectId}/sessions/{sessionId}/favorite', tags=["sessions"]) @app.get('/{projectId}/sessions2/{sessionId}/favorite', tags=["sessions"]) def add_remove_favorite_session2(projectId: int, sessionId: int, context: schemas.CurrentContext = Depends(OR_context)): @@ -44,6 +46,7 @@ def add_remove_favorite_session2(projectId: int, sessionId: int, session_id=sessionId)} +@app.get('/{projectId}/sessions/{sessionId}/assign', tags=["sessions"]) @app.get('/{projectId}/sessions2/{sessionId}/assign', tags=["sessions"]) def assign_session(projectId: int, sessionId, context: schemas.CurrentContext = Depends(OR_context)): data = sessions_assignments.get_by_session(project_id=projectId, session_id=sessionId, @@ -56,6 +59,7 @@ def assign_session(projectId: int, sessionId, context: schemas.CurrentContext = } +@app.get('/{projectId}/sessions/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"]) @app.get('/{projectId}/sessions2/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"]) def get_error_trace(projectId: int, sessionId: int, errorId: str, context: schemas.CurrentContext = Depends(OR_context)): @@ -67,6 +71,7 @@ def get_error_trace(projectId: int, sessionId: int, errorId: str, } +@app.get('/{projectId}/sessions/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"]) @app.get('/{projectId}/sessions2/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"]) def assign_session(projectId: int, sessionId: int, issueId: str, context: schemas.CurrentContext = Depends(OR_context)): @@ -79,6 +84,8 @@ def assign_session(projectId: int, sessionId: int, issueId: str, } +@app.post('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"]) +@app.put('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"]) @app.post('/{projectId}/sessions2/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"]) @app.put('/{projectId}/sessions2/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"]) def comment_assignment(projectId: int, sessionId: int, issueId: str, data: schemas.CommentAssignmentSchema = Body(...), @@ -825,6 +832,19 @@ def sessions_live(projectId: int, userId: str = None, context: schemas.CurrentCo return {'data': data} +@app.get('/{projectId}/assist/sessions/{sessionId}', tags=["assist"]) +def get_live_session(projectId: int, sessionId: str, context: schemas.CurrentContext = Depends(OR_context)): + data = assist.get_live_session_by_id(project_id=projectId, session_id=sessionId) + if data is None: + data = sessions.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True, + user_id=context.user_id, include_fav_viewed=True, group_metadata=True, live=False) + if data is None: + return {"errors": ["session not found"]} + if data.get("inDB"): + sessions_favorite_viewed.view_session(project_id=projectId, user_id=context.user_id, session_id=sessionId) + return {'data': data} + + @app.post('/{projectId}/heatmaps/url', tags=["heatmaps"]) def get_heatmaps_by_url(projectId: int, data: schemas.GetHeatmapPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): From ba9ca1d06044642648756f6a19a67d03eb6d1591 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 4 Apr 2022 12:03:30 +0200 Subject: [PATCH 38/87] feat(api): get save_request_payloads with the projects info --- api/chalicelib/core/projects.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/chalicelib/core/projects.py b/api/chalicelib/core/projects.py index c5ae912aa..ab0f33b4f 100644 --- a/api/chalicelib/core/projects.py +++ b/api/chalicelib/core/projects.py @@ -57,7 +57,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st cur.execute(f"""\ SELECT - s.project_id, s.name, s.project_key + s.project_id, s.name, s.project_key, s.save_request_payloads {',s.gdpr' if gdpr else ''} {',COALESCE((SELECT TRUE FROM public.sessions WHERE sessions.project_id = s.project_id LIMIT 1), FALSE) AS recorded' if recorded else ''} {',stack_integrations.count>0 AS stack_integrations' if stack_integrations else ''} @@ -109,7 +109,8 @@ def get_project(tenant_id, project_id, include_last_session=False, include_gdpr= SELECT s.project_id, s.project_key, - s.name + s.name, + s.save_request_payloads {",(SELECT max(ss.start_ts) FROM public.sessions AS ss WHERE ss.project_id = %(project_id)s) AS last_recorded_session_at" if include_last_session else ""} {',s.gdpr' if include_gdpr else ''} {tracker_query} From 6360712aebe2c5f129693aaf890fa874e9883223 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 4 Apr 2022 15:21:55 +0200 Subject: [PATCH 39/87] feat(api): fixed update custom_metrics with missing index --- api/chalicelib/core/custom_metrics.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 839ba4d72..cc457a0e2 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -148,10 +148,11 @@ def update(metric_id, user_id, project_id, data: schemas.UpdateCustomMetricsSche "metric_value": data.metric_value, "metric_format": data.metric_format} for i, s in enumerate(data.series): prefix = "u_" + if s.index is None: + s.index = i if s.series_id is None or s.series_id not in series_ids: n_series.append({"i": i, "s": s}) prefix = "n_" - s.index = i else: u_series.append({"i": i, "s": s}) u_series_ids.append(s.series_id) @@ -198,9 +199,7 @@ def update(metric_id, user_id, project_id, data: schemas.UpdateCustomMetricsSche AND project_id = %(project_id)s AND (user_id = %(user_id)s OR is_public) RETURNING metric_id;""", params) - cur.execute( - query - ) + cur.execute(query) return get(metric_id=metric_id, project_id=project_id, user_id=user_id) From 1549c554a7f2653aa06fde7b2707856ac030a246 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 4 Apr 2022 15:22:51 +0200 Subject: [PATCH 40/87] feat(ui) - dashboard - wip --- frontend/app/Router.js | 156 ++++---- .../app/components/Dashboard/NewDashboard.tsx | 78 +--- .../DashbaordListModal/DashbaordListModal.tsx | 8 +- .../DashboardMetricSelection.tsx | 5 +- .../DashboardRouter/DashboardRouter.tsx | 45 +++ .../components/DashboardRouter/index.ts | 1 + .../DashboardSideMenu/DashboardSideMenu.tsx | 26 +- .../DashboardView/DashboardView.tsx | 5 +- .../DashboardWidgetGrid.tsx | 2 +- .../MetricListItem/MetricListItem.tsx | 48 +++ .../components/MetricListItem/index.ts | 1 + .../components/MetricsList/MetricsList.tsx | 59 +-- .../components/MetricsView/MetricsView.tsx | 11 +- .../components/WidgetChart/WidgetChart.tsx | 27 ++ .../Dashboard/components/WidgetChart/index.ts | 1 + .../components/WidgetForm/WidgetForm.tsx | 58 +-- .../WidgetPreview/WidgetPreview.tsx | 6 +- .../components/WidgetView/WidgetView.tsx | 75 ++-- .../WidgetWrapper/WidgetWrapper.tsx | 11 +- .../{ => components}/WidgetWrapper/index.ts | 0 .../components/Dashboard/store/dashboard.ts | 2 +- .../Dashboard/store/dashboardStore.ts | 6 +- .../app/components/Dashboard/store/index.ts | 2 +- .../app/components/Dashboard/store/widget.ts | 1 + .../app/components/ui/BackLink/BackLink.js | 2 +- frontend/app/initialize.js | 3 - frontend/app/mstore/dashboardStore.ts | 341 ++++++++++++++++++ frontend/app/mstore/index.tsx | 17 +- frontend/app/mstore/metricStore.ts | 177 +++++++++ frontend/app/mstore/types/dashboard.ts | 152 ++++++++ frontend/app/mstore/types/filter.ts | 61 ++++ frontend/app/mstore/types/filterItem.ts | 66 ++++ frontend/app/mstore/types/filterSeries.ts | 38 ++ frontend/app/mstore/types/widget.ts | 137 +++++++ frontend/app/routes.js | 18 +- frontend/app/services/DashboardService.ts | 12 +- frontend/app/services/MetricService.ts | 91 +++++ frontend/app/services/index.ts | 4 +- 38 files changed, 1473 insertions(+), 280 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardRouter/index.ts create mode 100644 frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx create mode 100644 frontend/app/components/Dashboard/components/MetricListItem/index.ts create mode 100644 frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx create mode 100644 frontend/app/components/Dashboard/components/WidgetChart/index.ts rename frontend/app/components/Dashboard/{ => components}/WidgetWrapper/WidgetWrapper.tsx (85%) rename frontend/app/components/Dashboard/{ => components}/WidgetWrapper/index.ts (100%) create mode 100644 frontend/app/mstore/dashboardStore.ts create mode 100644 frontend/app/mstore/metricStore.ts create mode 100644 frontend/app/mstore/types/dashboard.ts create mode 100644 frontend/app/mstore/types/filter.ts create mode 100644 frontend/app/mstore/types/filterItem.ts create mode 100644 frontend/app/mstore/types/filterSeries.ts create mode 100644 frontend/app/mstore/types/widget.ts create mode 100644 frontend/app/services/MetricService.ts diff --git a/frontend/app/Router.js b/frontend/app/Router.js index b8c7f7a03..a31391127 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -1,3 +1,4 @@ +import React, { lazy, Suspense } from 'react'; import { Switch, Route, Redirect } from 'react-router'; import { BrowserRouter, withRouter } from 'react-router-dom'; import { connect } from 'react-redux'; @@ -5,28 +6,29 @@ import { Notification } from 'UI'; import { Loader } from 'UI'; import { fetchUserInfo } from 'Duck/user'; import withSiteIdUpdater from 'HOCs/withSiteIdUpdater'; -import Login from 'Components/Login/Login'; -import ForgotPassword from 'Components/ForgotPassword/ForgotPassword'; -import UpdatePassword from 'Components/UpdatePassword/UpdatePassword'; -import ClientPure from 'Components/Client/Client'; -import OnboardingPure from 'Components/Onboarding/Onboarding'; -import SessionPure from 'Components/Session/Session'; -import LiveSessionPure from 'Components/Session/LiveSession'; -import AssistPure from 'Components/Assist'; -import BugFinderPure from 'Components/BugFinder/BugFinder'; -import DashboardPure from 'Components/Dashboard/NewDashboard'; +const Login = lazy(() => import('Components/Login/Login')); +const ForgotPassword = lazy(() => import('Components/ForgotPassword/ForgotPassword')); +const UpdatePassword = lazy(() => import('Components/UpdatePassword/UpdatePassword')); +const SessionPure = lazy(() => import('Components/Session/Session')); +const LiveSessionPure = lazy(() => import('Components/Session/LiveSession')); +const OnboardingPure = lazy(() => import('Components/Onboarding/Onboarding')); +const ClientPure = lazy(() => import('Components/Client/Client')); +const AssistPure = lazy(() => import('Components/Assist')); +const BugFinderPure = lazy(() => import('Components/BugFinder/BugFinder')); +const DashboardPure = lazy(() => import('Components/Dashboard/NewDashboard')); +const ErrorsPure = lazy(() => import('Components/Errors/Errors')); +const FunnelDetails = lazy(() => import('Components/Funnels/FunnelDetails')); +const FunnelIssueDetails = lazy(() => import('Components/Funnels/FunnelIssueDetails')); import WidgetViewPure from 'Components/Dashboard/components/WidgetView'; -import ErrorsPure from 'Components/Errors/Errors'; import Header from 'Components/Header/Header'; // import ResultsModal from 'Shared/Results/ResultsModal'; -import FunnelDetails from 'Components/Funnels/FunnelDetails'; -import FunnelIssueDetails from 'Components/Funnels/FunnelIssueDetails'; import { fetchList as fetchIntegrationVariables } from 'Duck/customField'; import { fetchList as fetchSiteList } from 'Duck/site'; import { fetchList as fetchAnnouncements } from 'Duck/announcements'; import { fetchList as fetchAlerts } from 'Duck/alerts'; import { fetchWatchdogStatus } from 'Duck/watchdogs'; import { dashboardService } from "App/services"; +import { withStore } from 'App/mstore' import APIClient from './api_client'; import * as routes from './routes'; @@ -49,9 +51,12 @@ const FunnelIssue = withSiteIdUpdater(FunnelIssueDetails); const withSiteId = routes.withSiteId; const withObTab = routes.withObTab; +const METRICS_PATH = routes.metrics(); +const METRICS_DETAILS = routes.metricDetails(); + const DASHBOARD_PATH = routes.dashboard(); const DASHBOARD_SELECT_PATH = routes.dashboardSelected(); -const DASHBOARD_METRICS_PATH = routes.dashboardMetricCreate(); +const DASHBOARD_METRIC_CREATE_PATH = routes.dashboardMetricCreate(); // const WIDGET_PATAH = routes.dashboardMetric(); const SESSIONS_PATH = routes.sessions(); @@ -69,6 +74,7 @@ const CLIENT_PATH = routes.client(); const ONBOARDING_PATH = routes.onboarding(); const ONBOARDING_REDIRECT_PATH = routes.onboarding(OB_DEFAULT_TAB); +@withStore @withRouter @connect((state) => { const siteId = state.getIn([ 'user', 'siteId' ]); @@ -115,7 +121,8 @@ class Router extends React.Component { fetchInitialData = () => { Promise.all([ this.props.fetchUserInfo().then(() => { - dashboardService.initClient(); + const { mstore } = this.props + mstore.initClient(); this.props.fetchIntegrationVariables() }), this.props.fetchSiteList().then(() => { @@ -161,65 +168,74 @@ class Router extends React.Component { {!hideHeader &&
} - - - - { - const client = new APIClient(jwt); - switch (location.pathname) { - case '/integrations/slack': - client.post('integrations/slack/add', { - code: location.search.split('=')[ 1 ], - state: tenantId, - }); - break; + }> + + + + { + const client = new APIClient(jwt); + switch (location.pathname) { + case '/integrations/slack': + client.post('integrations/slack/add', { + code: location.search.split('=')[ 1 ], + state: tenantId, + }); + break; + } + return ; } - return ; } - } - /> - { onboarding && - - } - { siteIdList.length === 0 && - - } - - - - - - - {/* - - - - */} + /> + { onboarding && + + } + { siteIdList.length === 0 && + + } + + + - - - - - - - - - } /> - { routes.redirects.map(([ fr, to ]) => ( - - )) } - + + + + + + + {/* + + + + */} + + + + + + + + + + } /> + { routes.redirects.map(([ fr, to ]) => ( + + )) } + + + + + : + }> + + + + { !existingTenant && } + - : - - - - { !existingTenant && } - - ; + ; } } diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 832af0df0..56395f8a9 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -1,84 +1,46 @@ import React, { useEffect } from 'react'; -import { Switch, Route, Redirect } from 'react-router'; import withPageTitle from 'HOCs/withPageTitle'; -import { observer } from "mobx-react-lite"; +import { observer, useObserver } from "mobx-react-lite"; import { useStore } from 'App/mstore'; import { withRouter } from 'react-router-dom'; -import DashboardView from './components/DashboardView'; import { dashboardSelected, - dashboardMetricDetails, - dashboardMetricCreate, withSiteId, - dashboardMetrics, } from 'App/routes'; import DashboardSideMenu from './components/DashboardSideMenu'; -import WidgetView from './components/WidgetView'; -import MetricsView from './components/MetricsView'; +import { Loader } from 'UI'; +import DashboardRouter from './components/DashboardRouter'; function NewDashboard(props) { const { history, match: { params: { siteId, dashboardId, metricId } } } = props; const { dashboardStore } = useStore(); - const dashboard: any = dashboardStore.selectedDashboard; + const loading = useObserver(() => dashboardStore.isLoading); useEffect(() => { - dashboardStore.fetchList(); - dashboardStore.setSiteId(siteId); - }, []); - - useEffect(() => { - if (dashboardId) { - dashboardStore.selectDashboardById(dashboardId); - } - if (!dashboardId) { + dashboardStore.fetchList().then((resp) => { if (dashboardId) { dashboardStore.selectDashboardById(dashboardId); } else { - dashboardStore.selectDefaultDashboard().then((resp: any) => { - history.push(withSiteId(dashboardSelected(resp.dashboardId), siteId)); + dashboardStore.selectDefaultDashboard().then((b) => { + if (!history.location.pathname.includes('/metrics')) { + history.push(withSiteId(dashboardSelected(b.dashboardId), siteId)); + } }); } - } + }); }, []); - - + return ( - - -
-
- -
-
- -
+ +
+
+
- - - - - { dashboardId && ( - <> - -
-
- -
-
- -
-
-
- - - {/* - - - */} - {/* */} - - )} - +
+ +
+
+
); } diff --git a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx index 49cef58f0..8a5892cb3 100644 --- a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx +++ b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import Modal from 'react-modal'; import { useStore } from 'App/mstore'; import { SideMenuitem, SideMenuHeader, Icon, Button } from 'UI'; @@ -12,19 +11,16 @@ function DashbaordListModal(props) {
Dashboards
{dashboards.map((item: any) => ( - //
- // {item.name} - //
onItemClick(item)} + // onClick={() => onItemClick(item)} // TODO add click handler leading = {(
-
+ {item.isPublic &&
} {item.isPinned &&
}
)} diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index d7c7cfa05..90729935f 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -1,9 +1,7 @@ import React from 'react'; -import WidgetWrapper from '../../WidgetWrapper'; -import { useDashboardStore } from '../../store/store'; +import WidgetWrapper from '../WidgetWrapper'; import { useObserver } from 'mobx-react-lite'; import cn from 'classnames'; -import { Button } from 'UI'; import { useStore } from 'App/mstore'; function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, unSelectCategory }) { @@ -30,7 +28,6 @@ function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, function DashboardMetricSelection(props) { const { dashboardStore } = useStore(); const widgetCategories = dashboardStore?.widgetCategories; - const widgetTemplates = dashboardStore?.widgetTemplates; const [activeCategory, setActiveCategory] = React.useState(widgetCategories[0]); const [selectedWidgets, setSelectedWidgets] = React.useState([]); const selectedWidgetIds = selectedWidgets.map((widget: any) => widget.widgetId); diff --git a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx new file mode 100644 index 000000000..82ab00a0e --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { Switch, Route } from 'react-router'; +import { withRouter } from 'react-router-dom'; + +import { + metrics, + metricDetails, + dashboardSelected, + dashboardMetricCreate, + withSiteId, +} from 'App/routes'; +import DashboardView from '../DashboardView'; +import MetricsView from '../MetricsView'; +import WidgetView from '../WidgetView'; + +interface Props { + history: any + match: any +} +function DashboardRouter(props: Props) { + const { match: { params: { siteId, dashboardId, metricId } } } = props; + return ( +
+ + + + + + + + + + + + + + + + + +
+ ); +} + +export default withRouter(DashboardRouter); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardRouter/index.ts b/frontend/app/components/Dashboard/components/DashboardRouter/index.ts new file mode 100644 index 000000000..62c27a8fd --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardRouter/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardRouter'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 646284d9e..c53a7a378 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -3,23 +3,23 @@ import React from 'react'; import { SideMenuitem, SideMenuHeader, Icon, Button } from 'UI'; import { useStore } from 'App/mstore'; import { withRouter } from 'react-router-dom'; -import { withSiteId, dashboardSelected, dashboardMetrics } from 'App/routes'; +import { withSiteId, dashboardSelected, metrics } from 'App/routes'; import { useModal } from 'App/components/Modal'; import DashbaordListModal from '../DashbaordListModal'; import DashboardModal from '../DashboardModal'; const SHOW_COUNT = 5; -function DashboardSideMenu(props) { +interface Props { + siteId: string + history: any +} +function DashboardSideMenu(props: Props) { + const { history, siteId } = props; const { hideModal, showModal } = useModal(); - const { history } = props; const { dashboardStore } = useStore(); const dashboardId = dashboardStore.selectedDashboard?.dashboardId; const dashboardsPicked = dashboardStore.dashboards.slice(0, SHOW_COUNT); const remainingDashboardsCount = dashboardStore.dashboards.length - SHOW_COUNT; - - // React.useEffect(() => { - // showModal(, {}); - // }, []); const redirect = (path) => { history.push(path); @@ -27,7 +27,7 @@ function DashboardSideMenu(props) { const onItemClick = (dashboard) => { dashboardStore.selectDashboardById(dashboard.dashboardId); - const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(dashboardStore.siteId)); + const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(siteId)); history.push(path); }; @@ -36,7 +36,7 @@ function DashboardSideMenu(props) { showModal(, {}) } - return ( + return useObserver(() => (
{dashboardsPicked.map((item: any) => ( @@ -48,7 +48,7 @@ function DashboardSideMenu(props) { onClick={() => onItemClick(item)} leading = {(
-
+ {item.isPublic &&
} {item.isPinned &&
}
)} @@ -79,7 +79,7 @@ function DashboardSideMenu(props) { id="menu-manage-alerts" title="Metrics" iconName="bar-chart-line" - onClick={() => redirect(withSiteId(dashboardMetrics(), dashboardStore.siteId))} + onClick={() => redirect(withSiteId(metrics(), siteId))} />
@@ -92,7 +92,7 @@ function DashboardSideMenu(props) { />
- ); + )); } -export default withRouter(observer(DashboardSideMenu)); \ No newline at end of file +export default withRouter(DashboardSideMenu); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index c229ae104..4ac3380c6 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -7,9 +7,10 @@ import withModal from 'App/components/Modal/withModal'; import DashboardWidgetGrid from '../DashboardWidgetGrid'; interface Props { - + siteId: number; } function DashboardView(props: Props) { + const { siteId } = props; const { dashboardStore } = useStore(); const dashboard: any = dashboardStore.selectedDashboard @@ -18,7 +19,7 @@ function DashboardView(props: Props) {
- +
Right diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx index 237d5fcb4..b52a25329 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useStore } from 'App/mstore'; -import WidgetWrapper from '../../WidgetWrapper'; +import WidgetWrapper from '../WidgetWrapper'; import { NoContent, Button, Loader } from 'UI'; import { useObserver } from 'mobx-react-lite'; diff --git a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx new file mode 100644 index 000000000..cafcf53d8 --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Icon, NoContent, Label, Link, Pagination } from 'UI'; + +interface Props { + metric: any; +} + +function DashboardLink({ dashboards}) { + return ( + dashboards.map(dashboard => ( + +
+
·
+ {dashboard.name} +
+ + )) + ); +} + +function MetricListItem(props: Props) { + const { metric } = props; + return ( +
+
+ + {metric.name} + +
+
+
+ + {/*
+
·
+ Dashboards +
*/} +
+
{metric.owner}
+ {/*
+ + {metric.isPublic ? 'Team' : 'Private'} +
*/} +
Last Modified
+
+ ); +} + +export default MetricListItem; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricListItem/index.ts b/frontend/app/components/Dashboard/components/MetricListItem/index.ts new file mode 100644 index 000000000..b4c506a23 --- /dev/null +++ b/frontend/app/components/Dashboard/components/MetricListItem/index.ts @@ -0,0 +1 @@ +export { default } from './MetricListItem'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index 656ce2d3a..9ef809261 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -1,24 +1,20 @@ import { useObserver } from 'mobx-react-lite'; import React from 'react'; -import { Icon, NoContent, Label, Link, Pagination } from 'UI'; +import { NoContent, Pagination } from 'UI'; import { useStore } from 'App/mstore'; import { getRE } from 'App/utils'; +import MetricListItem from '../MetricListItem'; interface Props { } function MetricsList(props: Props) { - const { dashboardStore } = useStore(); - const widgets = dashboardStore.widgets; - const lenth = widgets.length; - const currentPage = useObserver(() => dashboardStore.metricsPage); - const metricsSearch = useObserver(() => dashboardStore.metricsSearch); - - const filterRE = getRE(metricsSearch, 'i'); - const list = widgets.filter(w => filterRE.test(w.name)) + const { metricStore } = useStore(); + const metrics = useObserver(() => metricStore.metrics); + const lenth = metrics.length; - const totalPages = list.length; - const pageSize = dashboardStore.metricsPageSize; - const start = (currentPage - 1) * pageSize; - const end = currentPage * pageSize; + const metricsSearch = useObserver(() => metricStore.metricsSearch); + const filterRE = getRE(metricsSearch, 'i'); + const list = metrics.filter(w => filterRE.test(w.name)); + return useObserver(() => ( @@ -28,44 +24,21 @@ function MetricsList(props: Props) {
Type
Dashboards
Owner
-
Visibility & Edit Access
+ {/*
Visibility & Edit Access
*/}
Last Modified
- {list.slice(start, end).map((metric: any) => ( -
-
- - {metric.name} - -
-
-
Dashboards
-
{metric.owner}
-
- {metric.isPrivate ? ( -
- - Private -
- ) : ( -
- - Team -
- )} -
-
Last Modified
-
+ {list.map((metric: any) => ( + ))}
dashboardStore.updateKey('metricsPage', page)} - limit={pageSize} + page={metricStore.page} + totalPages={Math.ceil(lenth / metricStore.pageSize)} + onPageChange={(page) => metricStore.updateKey('page', page)} + limit={metricStore.pageSize} debounceRequest={100} />
diff --git a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx index b8ec01d25..18c1204bb 100644 --- a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx @@ -3,9 +3,16 @@ import { Button, PageTitle, Icon, Link } from 'UI'; import { withSiteId, dashboardMetricCreate } from 'App/routes'; import MetricsList from '../MetricsList'; import MetricsSearch from '../MetricsSearch'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; function MetricsView(props) { - return ( + const { metricStore } = useStore(); + + React.useEffect(() => { + metricStore.fetchList(); + }, []); + return useObserver(() => (
@@ -16,7 +23,7 @@ function MetricsView(props) {
- ); + )); } export default MetricsView; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx new file mode 100644 index 000000000..53f15faf5 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +interface Props { + metric: any; +} +function WidgetChart(props: Props) { + const { metric } = props; + const renderChart = () => { + const { metricType } = metric; + if (metricType === 'timeseries') { + return
Chart
; + } + + if (metricType === 'table') { + return
Table
; + } + + return
Unknown
; + } + return ( +
+ {renderChart()} +
+ ); +} + +export default WidgetChart; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetChart/index.ts b/frontend/app/components/Dashboard/components/WidgetChart/index.ts new file mode 100644 index 000000000..0ea9108ea --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetChart/index.ts @@ -0,0 +1 @@ +export { default } from './WidgetChart' diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 9cd0002dc..373c399c9 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -7,16 +7,19 @@ import { useObserver } from 'mobx-react-lite'; import { HelpText, Button, Icon } from 'UI' import FilterSeries from '../FilterSeries'; import { withRouter } from 'react-router-dom'; +import { confirm } from 'UI/Confirmation'; interface Props { history: any; match: any; + onDelete: () => void; } function WidgetForm(props: Props) { const { history, match: { params: { siteId, dashboardId, metricId } } } = props; - const { dashboardStore } = useStore(); - const metric: any = dashboardStore.currentWidget; + console.log('WidgetForm params', props.match.params); + const { metricStore } = useStore(); + const metric: any = metricStore.instance; const timeseriesOptions = metricOf.filter(i => i.type === 'timeseries'); const tableOptions = metricOf.filter(i => i.type === 'table'); @@ -24,31 +27,44 @@ function WidgetForm(props: Props) { const isTimeSeries = metric.metricType === 'timeseries'; const _issueOptions = [{ text: 'All', value: 'all' }].concat(issueOptions); - const write = ({ target: { value, name } }) => dashboardStore.editWidget({ [ name ]: value }); + const write = ({ target: { value, name } }) => metricStore.merge({ [ name ]: value }); const writeOption = (e, { value, name }) => { - dashboardStore.editWidget({ [ name ]: value }); + metricStore.merge({ [ name ]: value }); if (name === 'metricValue') { - dashboardStore.editWidget({ metricValue: [value] }); + metricStore.merge({ metricValue: [value] }); } if (name === 'metricOf') { if (value === FilterKey.ISSUE) { - dashboardStore.editWidget({ metricValue: ['all'] }); + metricStore.merge({ metricValue: ['all'] }); } } if (name === 'metricType') { if (value === 'timeseries') { - dashboardStore.editWidget({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }); + metricStore.merge({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }); } else if (value === 'table') { - dashboardStore.editWidget({ metricOf: tableOptions[0].value, viewType: 'table' }); + metricStore.merge({ metricOf: tableOptions[0].value, viewType: 'table' }); } } }; const onSave = () => { - dashboardStore.saveMetric(metric, dashboardId); + metricStore.save(metric, dashboardId); + } + + const onDelete = async () => { + if (await confirm({ + header: 'Confirm', + confirmButton: 'Yes, Delete', + confirmation: `Are you sure you want to permanently delete this metric?` + })) { + metricStore.delete(metric).then(props.onDelete); + // props.remove(instance.alertId).then(() => { + // toggleForm(null, false); + // }); + } } return useObserver(() => ( @@ -88,15 +104,15 @@ function WidgetForm(props: Props) { )} {metric.metricOf === FilterKey.ISSUE && ( - <> - issue type - - + <> + issue type + + )} {metric.metricType === 'table' && ( @@ -154,9 +170,9 @@ function WidgetForm(props: Props) { Save
- {metric.widgetId && ( + {metric.exists() && ( <> - @@ -172,4 +188,4 @@ function WidgetForm(props: Props) { )); } -export default withRouter(WidgetForm); \ No newline at end of file +export default WidgetForm; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index 1e39c2c63..11b9e326c 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -1,6 +1,6 @@ import React from 'react'; import cn from 'classnames'; -import WidgetWrapper from '../../WidgetWrapper'; +import WidgetWrapper from '../WidgetWrapper'; import { useStore } from 'App/mstore'; import { Loader, NoContent, SegmentSelection, Icon } from 'UI'; import DateRange from 'Shared/DateRange'; @@ -11,8 +11,8 @@ interface Props { } function WidgetPreview(props: Props) { const { className = '' } = props; - const { dashboardStore } = useStore(); - const metric: any = dashboardStore.currentWidget; + const { metricStore } = useStore(); + const metric: any = metricStore.instance; const isTimeSeries = metric.metricType === 'timeseries'; const isTable = metric.metricType === 'table'; diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index 4ad9eaa00..a63c58beb 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -4,38 +4,65 @@ import { useStore } from 'App/mstore'; import WidgetForm from '../WidgetForm'; import WidgetPreview from '../WidgetPreview'; import WidgetSessions from '../WidgetSessions'; -import { Icon } from 'UI'; - +import { Icon, BackLink, Loader } from 'UI'; +import { useObserver } from 'mobx-react-lite'; +import { withSiteId } from 'App/routes'; interface Props { - + history: any; + match: any + siteId: any } function WidgetView(props: Props) { + const { match: { params: { siteId, dashboardId, metricId } } } = props; const [expanded, setExpanded] = useState(true); - const { dashboardStore } = useStore(); - const widget = dashboardStore.currentWidget; - return ( -
-
-
-

{widget.name}

-
-
setExpanded(!expanded)} - className="flex items-center cursor-pointer select-none" - > - {expanded ? 'Collapse' : 'Expand'} - + const { metricStore } = useStore(); + const widget = useObserver(() => metricStore.instance); + const loading = useObserver(() => metricStore.isLoading); + + React.useEffect(() => { + if (metricId && metricId !== 'create') { + metricStore.fetch(metricId).then((metric) => { + // metricStore.init(metric) + }); + } else { + metricStore.init(); + } + }, []) + + const onBackHandler = () => { + if (dashboardId) { + props.history.push(withSiteId(`/dashboard/${dashboardId}`, siteId)); + } { + props.history.push(withSiteId(`/metrics`, siteId)); + } + } + + return useObserver(() => ( + +
+ +
+
+

{widget.name}

+
+
setExpanded(!expanded)} + className="flex items-center cursor-pointer select-none" + > + {expanded ? 'Collapse' : 'Expand'} + +
+ + { expanded && }
- { expanded && } + +
- - - -
- ); + + )); } -export default withRouter(WidgetView); \ No newline at end of file +export default WidgetView; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx similarity index 85% rename from frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx rename to frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 86ce18b36..92c3f42fd 100644 --- a/frontend/app/components/Dashboard/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -1,8 +1,8 @@ import React, { useRef } from 'react'; -import { useDashboardStore } from '../store/store'; import cn from 'classnames'; import { ItemMenu } from 'UI'; import { useDrag, useDrop } from 'react-dnd'; +import WidgetChart from '../WidgetChart'; interface Props { className?: string; @@ -46,7 +46,7 @@ function WidgetWrapper(props: Props) { style={{ userSelect: 'none', opacity: isDragging ? 0.5 : 1, - borderColor: canDrop && isOver ? '#394EFF' : '#EEE', + borderColor: canDrop && isOver ? '#394EFF' : '', }} ref={dragDropRef} > @@ -54,7 +54,7 @@ function WidgetWrapper(props: Props) {
- {widget.name} - {widget.position} + {widget.name}
- {/* */}
- +
{/* */}
diff --git a/frontend/app/components/Dashboard/WidgetWrapper/index.ts b/frontend/app/components/Dashboard/components/WidgetWrapper/index.ts similarity index 100% rename from frontend/app/components/Dashboard/WidgetWrapper/index.ts rename to frontend/app/components/Dashboard/components/WidgetWrapper/index.ts diff --git a/frontend/app/components/Dashboard/store/dashboard.ts b/frontend/app/components/Dashboard/store/dashboard.ts index d2e4596d5..dd253a6d3 100644 --- a/frontend/app/components/Dashboard/store/dashboard.ts +++ b/frontend/app/components/Dashboard/store/dashboard.ts @@ -82,13 +82,13 @@ export default class Dashboard implements IDashboard { this.dashboardId = json.dashboardId this.name = json.name this.isPublic = json.isPublic + this.isPinned = json.isPinned this.widgets = json.widgets ? json.widgets.map(w => new Widget().fromJson(w)) : [] }) return this } validate() { - console.log('called...') return this.isValid = this.name.length > 0 } diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts index 8ca3f6c2e..65dbf861b 100644 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ b/frontend/app/components/Dashboard/store/dashboardStore.ts @@ -167,12 +167,12 @@ export default class DashboardStore implements IDashboardSotre { save(dashboard: IDashboard): Promise { this.isSaving = true const isCreating = !dashboard.dashboardId - return dashboardService.saveDashboard(dashboard).then(response => { + return dashboardService.saveDashboard(dashboard).then(_dashboard => { runInAction(() => { if (isCreating) { - this.addDashboard(response.data) + this.addDashboard(_dashboard) } else { - this.updateDashboard(response.data) + this.updateDashboard(_dashboard) } }) }).finally(() => { diff --git a/frontend/app/components/Dashboard/store/index.ts b/frontend/app/components/Dashboard/store/index.ts index bc920972d..6baa3c043 100644 --- a/frontend/app/components/Dashboard/store/index.ts +++ b/frontend/app/components/Dashboard/store/index.ts @@ -1 +1 @@ -export { default } from './dashboardStore' \ No newline at end of file +export { default as DashboardStore } from './dashboardStore'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/widget.ts b/frontend/app/components/Dashboard/store/widget.ts index 481e7c969..5e624dd54 100644 --- a/frontend/app/components/Dashboard/store/widget.ts +++ b/frontend/app/components/Dashboard/store/widget.ts @@ -32,6 +32,7 @@ export interface IWidget { update(data: any): void } export default class Widget implements IWidget { + public static get ID_KEY():string { return "widgetId" } widgetId: any = undefined name: string = "New Metric" metricType: string = "timeseries" diff --git a/frontend/app/components/ui/BackLink/BackLink.js b/frontend/app/components/ui/BackLink/BackLink.js index db47ae93f..c66be5d15 100644 --- a/frontend/app/components/ui/BackLink/BackLink.js +++ b/frontend/app/components/ui/BackLink/BackLink.js @@ -6,7 +6,7 @@ export default function BackLink ({ className, to, onClick, label, vertical = false, style }) { const children = ( -
+
{ label &&
{ label }
}
diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index df06dbbbe..9e73942f7 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -10,9 +10,6 @@ import { ModalProvider } from './components/Modal'; import ModalRoot from './components/Modal/ModalRoot'; import { HTML5Backend } from 'react-dnd-html5-backend' import { DndProvider } from 'react-dnd' -import Modal from 'react-modal'; - -Modal.setAppElement('#modal-root'); document.addEventListener('DOMContentLoaded', () => { render( diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts new file mode 100644 index 000000000..28e7197fb --- /dev/null +++ b/frontend/app/mstore/dashboardStore.ts @@ -0,0 +1,341 @@ +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import Dashboard, { IDashboard } from "./types/dashboard" +import Widget, { IWidget } from "./types/widget"; +import { dashboardService } from "App/services"; + +export interface IDashboardSotre { + dashboards: IDashboard[] + widgetTemplates: any[] + selectedDashboard: IDashboard | null + dashboardInstance: IDashboard + siteId: any + currentWidget: Widget + widgetCategories: any[] + widgets: Widget[] + metricsPage: number + metricsPageSize: number + metricsSearch: string + + isLoading: boolean + isSaving: boolean + + initDashboard(dashboard?: IDashboard): void + updateKey(key: string, value: any): void + resetCurrentWidget(): void + editWidget(widget: any): void + fetchList(): Promise + fetch(dashboardId: string) + save(dashboard: IDashboard): Promise + saveDashboardWidget(dashboard: Dashboard, widget: Widget) + delete(dashboard: IDashboard) + toJson(): void + fromJson(json: any): void + initDashboard(dashboard: IDashboard): void + addDashboard(dashboard: IDashboard): void + removeDashboard(dashboard: IDashboard): void + getDashboard(dashboardId: string): void + getDashboardIndex(dashboardId: string): number + getDashboardCount(): void + getDashboardIndexByDashboardId(dashboardId: string): number + updateDashboard(dashboard: IDashboard): void + selectDashboardById(dashboardId: string): void + setSiteId(siteId: any): void + selectDefaultDashboard(): Promise + + saveMetric(metric: IWidget, dashboardId?: string): Promise +} +export default class DashboardStore implements IDashboardSotre { + siteId: any = null + // Dashbaord / Widgets + dashboards: Dashboard[] = [] + widgetTemplates: any[] = [] + selectedDashboard: Dashboard | null = new Dashboard() + dashboardInstance: IDashboard = new Dashboard() + currentWidget: Widget = new Widget() + widgetCategories: any[] = [] + widgets: Widget[] = [] + + // Metrics + metricsPage: number = 1 + metricsPageSize: number = 10 + metricsSearch: string = '' + + // Loading states + isLoading: boolean = true + isSaving: boolean = false + + constructor() { + makeAutoObservable(this, { + resetCurrentWidget: action, + addDashboard: action, + removeDashboard: action, + updateDashboard: action, + getDashboard: action, + getDashboardIndex: action, + getDashboardByIndex: action, + getDashboardCount: action, + getDashboardIndexByDashboardId: action, + selectDashboardById: action, + selectDefaultDashboard: action, + toJson: action, + fromJson: action, + setSiteId: action, + editWidget: action, + updateKey: action, + }) + + + // TODO remove this sample data + // this.dashboards = sampleDashboards + + // for (let i = 0; i < 15; i++) { + // const widget: any= {}; + // widget.widgetId = `${i}` + // widget.name = `Widget ${i}`; + // widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; + // this.widgets.push(widget) + // } + + for (let i = 0; i < 4; i++) { + const cat: any = { + name: `Category ${i + 1}`, + categoryId: i, + description: `Category ${i + 1} description`, + widgets: [] + } + + const randomNumber = Math.floor(Math.random() * (5 - 2 + 1)) + 2 + for (let j = 0; j < randomNumber; j++) { + const widget: any= {}; + widget.widgetId = `${i}-${j}` + widget.name = `Widget ${i}-${j}`; + widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; + cat.widgets.push(widget); + } + + this.widgetCategories.push(cat) + } + } + + findByIds(ids: string[]) { + return this.dashboards.filter(d => ids.includes(d.dashboardId)) + } + + initDashboard(dashboard: Dashboard) { + this.dashboardInstance = dashboard || new Dashboard() + } + + updateKey(key: any, value: any) { + this[key] = value + } + + resetCurrentWidget() { + this.currentWidget = new Widget() + } + + editWidget(widget: any) { + this.currentWidget.update(widget) + } + + fetchList(): Promise { + this.isLoading = true + + return dashboardService.getDashboards() + .then((list: any) => { + runInAction(() => { + this.dashboards = list.map(d => new Dashboard().fromJson(d)) + }) + }).finally(() => { + runInAction(() => { + this.isLoading = false + }) + }) + } + + fetch(dashboardId: string) { + this.isLoading = true + dashboardService.getDashboard(dashboardId).then(response => { + runInAction(() => { + this.selectedDashboard = new Dashboard().fromJson(response) + }) + }).finally(() => { + runInAction(() => { + this.isLoading = false + }) + }) + } + + save(dashboard: IDashboard): Promise { + this.isSaving = true + const isCreating = !dashboard.dashboardId + return dashboardService.saveDashboard(dashboard).then(_dashboard => { + runInAction(() => { + if (isCreating) { + this.addDashboard(_dashboard) + } else { + this.updateDashboard(_dashboard) + } + }) + }).finally(() => { + runInAction(() => { + this.isSaving = false + }) + }) + } + + saveMetric(metric: IWidget, dashboardId: string): Promise { + const isCreating = !metric.widgetId + return dashboardService.saveMetric(metric, dashboardId).then(metric => { + runInAction(() => { + if (isCreating) { + this.selectedDashboard?.widgets.push(metric) + } else { + this.selectedDashboard?.widgets.map(w => { + if (w.widgetId === metric.widgetId) { + w.update(metric) + } + }) + } + }) + }) + } + + saveDashboardWidget(dashboard: Dashboard, widget: Widget) { + widget.validate() + if (widget.isValid) { + this.isLoading = true + } + } + + delete(dashboard: Dashboard) { + this.isLoading = true + } + + toJson() { + return { + dashboards: this.dashboards.map(d => d.toJson()) + } + } + + fromJson(json: any) { + runInAction(() => { + this.dashboards = json.dashboards.map(d => new Dashboard().fromJson(d)) + }) + return this + } + + addDashboard(dashboard: Dashboard) { + this.dashboards.push(dashboard) + } + + removeDashboard(dashboard: Dashboard) { + this.dashboards = this.dashboards.filter(d => d !== dashboard) + } + + getDashboard(dashboardId: string) { + return this.dashboards.find(d => d.dashboardId === dashboardId) + } + + getDashboardIndex(dashboardId: string) { + return this.dashboards.findIndex(d => d.dashboardId === dashboardId) + } + + getDashboardByIndex(index: number) { + return this.dashboards[index] + } + + getDashboardCount() { + return this.dashboards.length + } + + getDashboardIndexByDashboardId(dashboardId: string) { + return this.dashboards.findIndex(d => d.dashboardId === dashboardId) + } + + updateDashboard(dashboard: Dashboard) { + const index = this.dashboards.findIndex(d => d.dashboardId === dashboard.dashboardId) + if (index >= 0) { + this.dashboards[index] = dashboard + } + } + + selectDashboardById = (dashboardId: any) => { + this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || new Dashboard(); + if (this.selectedDashboard.dashboardId) { + this.fetch(this.selectedDashboard.dashboardId) + } + } + + setSiteId = (siteId: any) => { + this.siteId = siteId + } + + selectDefaultDashboard = (): Promise => { + return new Promise((resolve, reject) => { + if (this.dashboards.length > 0) { + const pinnedDashboard = this.dashboards.find(d => d.isPinned) + if (pinnedDashboard) { + console.log('selecting pined dashboard') + this.selectedDashboard = pinnedDashboard + } else { + console.log('selecting first dashboard') + this.selectedDashboard = this.dashboards[0] + } + } + if (this.selectedDashboard) { + resolve(this.selectedDashboard) + } + + reject(new Error("No dashboards found")) + }) + } +} + +function getRandomWidget() { + const widget = new Widget(); + widget.widgetId = Math.floor(Math.random() * 100); + widget.name = randomMetricName(); + // widget.type = "random"; + widget.colSpan = Math.floor(Math.random() * 2) + 1; + return widget; +} + +function generateRandomPlaceName() { + const placeNames = [ + "New York", + "Los Angeles", + "Chicago", + "Houston", + "Philadelphia", + "Phoenix", + "San Antonio", + "San Diego", + ] + return placeNames[Math.floor(Math.random() * placeNames.length)] +} + + +function randomMetricName () { + const metrics = ["Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders"]; + return metrics[Math.floor(Math.random() * metrics.length)]; +} + +function getRandomDashboard(id: any = null, isPinned = false) { + const dashboard = new Dashboard(); + dashboard.name = generateRandomPlaceName(); + dashboard.dashboardId = id ? id : Math.floor(Math.random() * 10); + dashboard.isPinned = isPinned; + for (let i = 0; i < 8; i++) { + const widget = getRandomWidget(); + widget.position = i; + dashboard.addWidget(widget); + } + return dashboard; +} + +const sampleDashboards = [ + getRandomDashboard(1, true), + getRandomDashboard(2), + getRandomDashboard(3), + getRandomDashboard(4), +] \ No newline at end of file diff --git a/frontend/app/mstore/index.tsx b/frontend/app/mstore/index.tsx index ce928b399..88f5472c2 100644 --- a/frontend/app/mstore/index.tsx +++ b/frontend/app/mstore/index.tsx @@ -1,10 +1,22 @@ import React from 'react'; -import DashboardStore, { IDashboardSotre } from 'App/components/Dashboard/store/DashboardStore'; +import DashboardStore, { IDashboardSotre } from './dashboardStore'; +import MetricStore, { IMetricStore } from './metricStore'; +import APIClient from 'App/api_client'; +import { dashboardService, metricService } from 'App/services'; export class RootStore { dashboardStore: IDashboardSotre; + metricStore: IMetricStore; + constructor() { this.dashboardStore = new DashboardStore(); + this.metricStore = new MetricStore(); + } + + initClient() { + const client = new APIClient(); + dashboardService.initClient(client) + metricService.initClient(client) } } @@ -19,6 +31,5 @@ export const StoreProvider = ({ children, store }) => { export const useStore = () => React.useContext(StoreContext); export const withStore = (Component) => (props) => { - return ; + return ; }; - diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts new file mode 100644 index 000000000..e312ee58c --- /dev/null +++ b/frontend/app/mstore/metricStore.ts @@ -0,0 +1,177 @@ +import { makeAutoObservable, runInAction, observable, action, reaction, computed } from "mobx" +import Widget, { IWidget } from "./types/widget"; +import { metricService } from "App/services"; + +export interface IMetricStore { + paginatedList: any; + + isLoading: boolean + isSaving: boolean + + metrics: IWidget[] + instance: IWidget + + page: number + pageSize: number + metricsSearch: string + sort: any + + // State Actions + init(metric?: IWidget|null): void + updateKey(key: string, value: any): void + merge(object: any): void + reset(meitricId: string): void + addToList(metric: IWidget): void + updateInList(metric: IWidget): void + findById(metricId: string): void + removeById(metricId: string): void + + // API + save(metric: IWidget, dashboardId?: string): Promise + fetchList(): void + fetch(metricId: string) + delete(metric: IWidget) +} + +export default class MetricStore implements IMetricStore { + isLoading: boolean = false + isSaving: boolean = false + + metrics: IWidget[] = [] + instance: IWidget = new Widget() + + page: number = 1 + pageSize: number = 10 + metricsSearch: string = "" + sort: any = {} + + constructor() { + makeAutoObservable(this, { + isLoading: observable, + metrics: observable, + instance: observable, + page: observable, + pageSize: observable, + metricsSearch: observable, + sort: observable, + + init: action, + updateKey: action, + merge: action, + reset: action, + addToList: action, + updateInList: action, + findById: action, + removeById: action, + + save: action, + fetchList: action, + fetch: action, + delete: action, + + + paginatedList: computed, + }) + + // reaction( + // () => this.metricsSearch, + // (metricsSearch) => { // TODO filter the list for View + // console.log('metricsSearch', metricsSearch) + // this.page = 1 + // this.paginatedList() + // } + // ) + } + + // State Actions + init(metric?: IWidget|null) { + this.instance = metric || new Widget() + } + + updateKey(key: string, value: any) { + this.instance[key] = value + } + + merge(object: any) { + this.instance = Object.assign(this.instance, object) + } + + reset(id: string) { + const metric = this.findById(id) + if (metric) { + this.instance = metric + } + } + + addToList(metric: IWidget) { + this.metrics.push(metric) + } + + updateInList(metric: IWidget) { + const index = this.metrics.findIndex((m: IWidget) => m[Widget.ID_KEY] === metric[Widget.ID_KEY]) + if (index >= 0) { + this.metrics[index] = metric + } + } + + findById(id: string) { + return this.metrics.find(m => m[Widget.ID_KEY] === id) + } + + removeById(id: string): void { + this.metrics = this.metrics.filter(m => m[Widget.ID_KEY] !== id) + } + + get paginatedList(): IWidget[] { + console.log('here...' + this.page) + const start = (this.page - 1) * this.pageSize + const end = start + this.pageSize + return this.metrics.slice(start, end) + } + + // API Communication + save(metric: IWidget, dashboardId?: string) { + const wasCreating = !metric[Widget.ID_KEY] + this.isSaving = true + return metricService.saveMetric(metric, dashboardId) + .then(() => { + if (wasCreating) { + this.addToList(metric) + } else { + this.updateInList(metric) + } + }).finally(() => { + this.isSaving = false + }) + } + + fetchList() { + this.isLoading = true + return metricService.getMetrics() + .then(metrics => { + this.metrics = metrics + }).finally(() => { + this.isLoading = false + }) + } + + fetch(id: string) { + this.isLoading = true + return metricService.getMetric(id) + .then(metric => { + return this.instance = new Widget().fromJson(metric) + }).finally(() => { + this.isLoading = false + }) + } + + delete(metric: IWidget) { + this.isSaving = true + return metricService.deleteMetric(metric[Widget.ID_KEY]) + .then(() => { + this.removeById(metric[Widget.ID_KEY]) + }).finally(() => { + this.isSaving = false + }) + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts new file mode 100644 index 000000000..7f3095744 --- /dev/null +++ b/frontend/app/mstore/types/dashboard.ts @@ -0,0 +1,152 @@ +import { makeAutoObservable, observable, action, runInAction } from "mobx" +import Widget, { IWidget } from "./widget" + +export interface IDashboard { + dashboardId: any + name: string + isPublic: boolean + widgets: IWidget[] + isValid: boolean + isPinned: boolean + currentWidget: IWidget + + update(data: any): void + toJson(): any + fromJson(json: any): void + validate(): void + addWidget(widget: IWidget): void + removeWidget(widgetId: string): void + updateWidget(widget: IWidget): void + getWidget(widgetId: string): void + getWidgetIndex(widgetId: string) + getWidgetByIndex(index: number): void + getWidgetCount(): void + getWidgetIndexByWidgetId(widgetId: string): void + swapWidgetPosition(positionA: number, positionB: number): void + sortWidgets(): void +} +export default class Dashboard implements IDashboard { + public static get ID_KEY():string { return "dashboardId" } + dashboardId: any = undefined + name: string = "New Dashboard X" + isPublic: boolean = false + widgets: IWidget[] = [] + isValid: boolean = false + isPinned: boolean = false + currentWidget: IWidget = new Widget() + + constructor() { + makeAutoObservable(this, { + name: observable, + isPublic: observable, + widgets: observable, + isValid: observable, + + toJson: action, + fromJson: action, + addWidget: action, + removeWidget: action, + updateWidget: action, + getWidget: action, + getWidgetIndex: action, + getWidgetByIndex: action, + getWidgetCount: action, + getWidgetIndexByWidgetId: action, + validate: action, + sortWidgets: action, + swapWidgetPosition: action, + update: action, + }) + + this.validate(); + } + + update(data: any) { + runInAction(() => { + Object.assign(this, data) + }) + this.validate() + } + + toJson() { + return { + dashboardId: this.dashboardId, + name: this.name, + isPrivate: this.isPublic, + widgets: this.widgets.map(w => w.toJson()) + } + } + + fromJson(json: any) { + runInAction(() => { + this.dashboardId = json.dashboardId + this.name = json.name + this.isPublic = json.isPublic + this.isPinned = json.isPinned + this.widgets = json.widgets ? json.widgets.map(w => new Widget().fromJson(w)) : [] + }) + return this + } + + validate() { + return this.isValid = this.name.length > 0 + } + + addWidget(widget: IWidget) { + this.widgets.push(widget) + } + + removeWidget(widgetId: string) { + this.widgets = this.widgets.filter(w => w.widgetId !== widgetId) + } + + updateWidget(widget: IWidget) { + const index = this.widgets.findIndex(w => w.widgetId === widget.widgetId) + if (index >= 0) { + this.widgets[index] = widget + } + } + + getWidget(widgetId: string) { + return this.widgets.find(w => w.widgetId === widgetId) + } + + getWidgetIndex(widgetId: string) { + return this.widgets.findIndex(w => w.widgetId === widgetId) + } + + getWidgetByIndex(index: number) { + return this.widgets[index] + } + + getWidgetCount() { + return this.widgets.length + } + + getWidgetIndexByWidgetId(widgetId: string) { + return this.widgets.findIndex(w => w.widgetId === widgetId) + } + + swapWidgetPosition(positionA, positionB) { + console.log('swapWidgetPosition', positionA, positionB) + const widgetA = this.widgets[positionA] + const widgetB = this.widgets[positionB] + this.widgets[positionA] = widgetB + this.widgets[positionB] = widgetA + + widgetA.position = positionB + widgetB.position = positionA + } + + sortWidgets() { + this.widgets = this.widgets.sort((a, b) => { + if (a.position > b.position) { + return 1 + } else if (a.position < b.position) { + return -1 + } else { + return 0 + } + }) + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/filter.ts b/frontend/app/mstore/types/filter.ts new file mode 100644 index 000000000..5a7dcc5e9 --- /dev/null +++ b/frontend/app/mstore/types/filter.ts @@ -0,0 +1,61 @@ +import { filter } from "App/components/BugFinder/ManageFilters/savedFilterList.css" +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import { FilterKey, FilterType } from 'Types/filter/filterType' +import { filtersMap } from 'Types/filter/newFilter' +import FilterItem from "./filterItem" + +// console.log('filtersMap', filtersMap) + +export default class Filter { + public static get ID_KEY():string { return "filterId" } + name: string = '' + filters: any[] = [] + eventsOrder: string = 'then' + + constructor() { + makeAutoObservable(this, { + addFilter: action, + removeFilter: action, + updateKey: action, + }) + } + + addFilter(filter: any) { + filter.value = [""] + if (filter.hasOwnProperty('filters')) { + filter.filters = filter.filters.map(i => { + i.value = [""] + return new FilterItem(i) + }) + } + this.filters.push(new FilterItem(filter)) + } + + updateFilter(index: number, filter: any) { + this.filters[index] = new FilterItem(filter) + } + + updateKey(key, value) { + this[key] = value + } + + removeFilter(index: number) { + this.filters.splice(index, 1) + } + + fromJson(json) { + this.name = json.name + this.filters = json.filters.map(i => new FilterItem().fromJson(i)) + this.eventsOrder = json.eventsOrder + return this + } + + toJson() { + const json = { + name: this.name, + filters: this.filters.map(i => i.toJson()), + eventsOrder: this.eventsOrder, + } + return json + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/filterItem.ts b/frontend/app/mstore/types/filterItem.ts new file mode 100644 index 000000000..ea56020c5 --- /dev/null +++ b/frontend/app/mstore/types/filterItem.ts @@ -0,0 +1,66 @@ +import { filter } from "App/components/BugFinder/ManageFilters/savedFilterList.css" +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import { FilterKey, FilterType } from 'Types/filter/filterType' +import { filtersMap } from 'Types/filter/newFilter' + +export default class FilterItem { + type: string = '' + key: string = '' + label: string = '' + value: any = [""] + isEvent: boolean = false + operator: string = '' + source: string = '' + filters: FilterItem[] = [] + operatorOptions: any[] = [] + + constructor(data: any = {}) { + makeAutoObservable(this, { + type: observable, + key: observable, + value: observable, + }) + + this.merge(data) + } + + merge(data) { + Object.keys(data).forEach(key => { + this[key] = data[key] + }) + } + + fromJson(json, mainFilterKey = '') { + let _filter = filtersMap[json.type] + if (mainFilterKey) { + const mainFilter = filtersMap[mainFilterKey]; + const subFilterMap = {} + mainFilter.filters.forEach(option => { + subFilterMap[option.key] = option + }) + _filter = subFilterMap[json.type] + } + this.type = _filter.type + this.key = _filter.key + this.label = _filter.label + this.operatorOptions = _filter.operatorOptions + + this.value = json.value.length === 0 || !json.value ? [""] : json.value, + this.operator = json.operator + this.isEvent = _filter.isEvent + this.filters = _filter.type === FilterType.SUB_FILTERS && json.filters ? json.filters.map(i => new FilterItem().fromJson(i, json.type)) : [] + return this + } + + toJson() { + const json = { + type: this.key, + isEvent: this.isEvent, + value: this.value, + operator: this.operator, + source: this.source, + filters: this.filters.map(i => i.toJson()), + } + return json + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/filterSeries.ts b/frontend/app/mstore/types/filterSeries.ts new file mode 100644 index 000000000..c3051e612 --- /dev/null +++ b/frontend/app/mstore/types/filterSeries.ts @@ -0,0 +1,38 @@ +// import Filter from 'Types/filter'; +import Filter from './filter' +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" + +export default class FilterSeries { + public static get ID_KEY():string { return "seriesId" } + seriesId?: any = undefined + name: string = "Series 1" + filter: Filter = new Filter() + + constructor() { + makeAutoObservable(this, { + name: observable, + filter: observable, + + update: action, + }) + } + + update(key, value) { + this[key] = value + } + + fromJson(json) { + this.seriesId = json.seriesId + this.name = json.name + this.filter = new Filter().fromJson(json.filter) + return this + } + + toJson() { + return { + seriesId: this.seriesId, + name: this.name, + filter: this.filter.toJson(), + } + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts new file mode 100644 index 000000000..c55ce2e97 --- /dev/null +++ b/frontend/app/mstore/types/widget.ts @@ -0,0 +1,137 @@ +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import FilterSeries from "./filterSeries"; + +export interface IWidget { + metricId: string + widgetId: any + name: string + metricType: string + metricOf: string + metricValue: string + viewType: string + series: FilterSeries[] + sessions: [] + isPublic: boolean + owner: string + lastModified: Date + dashboards: any[] + dashboardIds: any[] + + position: number + data: any + isLoading: boolean + isValid: boolean + dashboardId: any + colSpan: number + + udpateKey(key: string, value: any): void + removeSeries(index: number): void + addSeries(): void + fromJson(json: any): void + toJson(): any + validate(): void + update(data: any): void + exists(): boolean +} +export default class Widget implements IWidget { + public static get ID_KEY():string { return "metricId" } + metricId: any = undefined + widgetId: any = undefined + name: string = "New Metric" + metricType: string = "timeseries" + metricOf: string = "sessionCount" + metricValue: string = "" + viewType: string = "lineChart" + series: FilterSeries[] = [] + sessions: [] = [] + isPublic: boolean = true + owner: string = "" + lastModified: Date = new Date() + dashboards: any[] = [] + dashboardIds: any[] = [] + + position: number = 0 + data: any = {} + isLoading: boolean = false + isValid: boolean = false + dashboardId: any = undefined + colSpan: number = 2 + + constructor() { + makeAutoObservable(this, { + widgetId: observable, + name: observable, + metricType: observable, + metricOf: observable, + position: observable, + data: observable, + isLoading: observable, + isValid: observable, + dashboardId: observable, + addSeries: action, + colSpan: observable, + + fromJson: action, + toJson: action, + validate: action, + update: action, + udpateKey: action, + }) + + const filterSeries = new FilterSeries() + this.series.push(filterSeries) + } + + udpateKey(key: string, value: any) { + this[key] = value + } + + removeSeries(index: number) { + this.series.splice(index, 1) + } + + addSeries() { + const series = new FilterSeries() + series.name = "Series " + (this.series.length + 1) + this.series.push(series) + } + + fromJson(json: any) { + console.log('json', json); + runInAction(() => { + this.metricId = json.metricId + this.widgetId = json.widgetId + this.name = json.name + this.data = json.data + this.series = json.series.map((series: any) => new FilterSeries().fromJson(series)), + this.dashboards = json.dashboards + }) + return this + } + + toJson() { + return { + metricId: this.metricId, + widgetId: this.widgetId, + metricOf: this.metricOf, + metricValue: this.metricValue, + metricType: this.metricType, + name: this.name, + series: this.series.map((series: any) => series.toJson()), + } + } + + validate() { + this.isValid = this.name.length > 0 + } + + update(data: any) { + runInAction(() => { + Object.assign(this, data) + }) + } + + exists() { + return this.metricId !== undefined + } +} \ No newline at end of file diff --git a/frontend/app/routes.js b/frontend/app/routes.js index 4aea022a4..08303f78b 100644 --- a/frontend/app/routes.js +++ b/frontend/app/routes.js @@ -105,9 +105,10 @@ export const dashboardMetrics = () => '/dashboard/metrics'; export const dashboardSelected = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }`, hash); export const dashboardMetricDetails = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash); -export const dashboardMetricCreate = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }/metric/create`, hash); -export const metricCreate = () => `/metric/create`; -export const metricDetails = (id = ':metricId', hash) => hashed(`/metric/${ id }`, hash); +export const dashboardMetricCreate = (dashboardId = ':dashboardId', hash) => hashed(`/dashboard/${ dashboardId }/metric/create`, hash); +export const metrics = () => `/metrics`; +export const metricCreate = () => `/metrics/create`; +export const metricDetails = (id = ':metricId', hash) => hashed(`/metrics/${ id }`, hash); export const RESULTS_QUERY_KEY = 'results'; @@ -120,12 +121,16 @@ const REQUIRED_SITE_ID_ROUTES = [ session(''), sessions(), assist(), + + metrics(), + metricDetails(''), + dashboard(''), dashboardSelected(''), dashboardMetrics(''), - // dashboardMetricCreate(''), + dashboardMetricCreate(''), dashboardMetricDetails(''), - metricCreate(''), + error(''), errors(), onboarding(''), @@ -158,8 +163,7 @@ const SITE_CHANGE_AVALIABLE_ROUTES = [ sessions(), assist(), dashboard(), - dashboardMetrics(''), - dashboardSelected(''), + metrics(), errors(), onboarding('') ]; diff --git a/frontend/app/services/DashboardService.ts b/frontend/app/services/DashboardService.ts index 871fabb3f..1e5e8974f 100644 --- a/frontend/app/services/DashboardService.ts +++ b/frontend/app/services/DashboardService.ts @@ -3,7 +3,7 @@ import APIClient from 'App/api_client'; import { IWidget } from "App/components/Dashboard/store/widget"; export interface IDashboardService { - initClient(): void + initClient(client?: APIClient) getWidgets(dashboardId: string): Promise getDashboards(): Promise @@ -20,15 +20,15 @@ export interface IDashboardService { } -export class DashboardService implements IDashboardService { +export default class DashboardService implements IDashboardService { private client: APIClient; constructor(client?: APIClient) { this.client = client ? client : new APIClient(); } - initClient() { - this.client = new APIClient(); + initClient(client?: APIClient) { + this.client = client || new APIClient(); } /** @@ -102,8 +102,8 @@ export class DashboardService implements IDashboardService { saveMetric(metric: IWidget, dashboardId?: string): Promise { const data = metric.toJson(); - const path = dashboardId ? `/metrics` : '/metrics'; // TODO change to /dashboards/:dashboardId/widgets - // const path = dashboardId ? `/dashboards/${dashboardId}/widgets` : '/widgets'; + // const path = dashboardId ? `/metrics` : '/metrics'; // TODO change to /dashboards/:dashboardId/widgets + const path = dashboardId ? `/dashboards/${dashboardId}/metrics` : '/metrics'; if (metric.widgetId) { return this.client.put(path + '/' + metric.widgetId, data) } else { diff --git a/frontend/app/services/MetricService.ts b/frontend/app/services/MetricService.ts new file mode 100644 index 000000000..99c935d47 --- /dev/null +++ b/frontend/app/services/MetricService.ts @@ -0,0 +1,91 @@ +import Widget, { IWidget } from "App/mstore/types/widget"; +import APIClient from 'App/api_client'; + +export interface IMetricService { + initClient(client?: APIClient): void; + + getMetrics(): Promise; + getMetric(metricId: string): Promise; + saveMetric(metric: IWidget, dashboardId?: string): Promise; + deleteMetric(metricId: string): Promise; + + getTemplates(): Promise; +} + +export default class MetricService implements IMetricService { + private client: APIClient; + + constructor(client?: APIClient) { + this.client = client ? client : new APIClient(); + } + + initClient(client?: APIClient) { + this.client = client || new APIClient(); + } + + /** + * Get all metrics. + * @returns {Promise} + */ + getMetrics(): Promise { + return this.client.get('/metrics') + .then(response => response.json()) + .then(response => response.data || []); + } + + /** + * Get a metric by metricId. + * @param metricId + * @returns {Promise} + */ + getMetric(metricId: string): Promise { + return this.client.get('/metrics/' + metricId) + .then(response => response.json()) + .then(response => response.data || {}); + } + + /** + * Save a metric. + * @param metric + * @returns + */ + saveMetric(metric: IWidget, dashboardId?: string): Promise { + const data = metric.toJson() + const isCreating = !data[Widget.ID_KEY]; + const method = isCreating ? 'post' : 'put'; + + if(dashboardId) { + const url = `/dashboards/${dashboardId}/metrics`; + return this.client[method](url, data) + .then(response => response.json()) + .then(response => response.data || {}); + } else { + const url = isCreating ? '/metrics' : '/metrics/' + data[Widget.ID_KEY]; + return this.client[method](url, data) + .then(response => response.json()) + .then(response => response.data || {}); + } + } + + /** + * Delete a metric. + * @param metricId + * @returns {Promise} + */ + deleteMetric(metricId: string): Promise { + return this.client.delete('/metrics/' + metricId) + .then(response => response.json()) + .then(response => response.data); + } + + + /** + * Get all templates. + * @returns {Promise} + */ + getTemplates(): Promise { + return this.client.get('/metrics/templates') + .then(response => response.json()) + .then(response => response.data || []); + } +} \ No newline at end of file diff --git a/frontend/app/services/index.ts b/frontend/app/services/index.ts index 49ad0eb0c..944877c16 100644 --- a/frontend/app/services/index.ts +++ b/frontend/app/services/index.ts @@ -1,3 +1,5 @@ -import { DashboardService, IDashboardService } from "./DashboardService"; +import DashboardService, { IDashboardService } from "./DashboardService"; +import MetricService, { IMetricService } from "./MetricService"; export const dashboardService: IDashboardService = new DashboardService(); +export const metricService: IMetricService = new MetricService(); \ No newline at end of file From ef4c9a5bf5def1ccc8f09e3054217725a37aa726 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 11:28:47 +0200 Subject: [PATCH 41/87] feat(db): added edited_at to metrics feat(db): defined all template metrics feat(db): support delta multi-execution feat(api): get edited_at with metric data feat(api): get owner_email with metric data feat(api): support of metrics areaChart --- api/chalicelib/core/custom_metrics.py | 15 +++- api/schemas.py | 1 + .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 87 ++++++++----------- .../db/init_dbs/postgresql/init_schema.sql | 1 + 4 files changed, 53 insertions(+), 51 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index cc457a0e2..51f8655a4 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -194,7 +194,8 @@ def update(metric_id, user_id, project_id, data: schemas.UpdateCustomMetricsSche SET name = %(name)s, is_public= %(is_public)s, view_type= %(view_type)s, metric_type= %(metric_type)s, metric_of= %(metric_of)s, metric_value= %(metric_value)s, - metric_format= %(metric_format)s + metric_format= %(metric_format)s, + edited_at = timezone('utc'::text, now()) WHERE metric_id = %(metric_id)s AND project_id = %(project_id)s AND (user_id = %(user_id)s OR is_public) @@ -224,6 +225,11 @@ def get_all(project_id, user_id, include_series=False): AND project_id = %(project_id)s AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards ) AS connected_dashboards ON (TRUE) + LEFT JOIN LATERAL (SELECT email AS owner_email + FROM users + WHERE deleted_at ISNULL + AND users.user_id = metrics.user_id + ) AS owner ON (TRUE) WHERE metrics.project_id = %(project_id)s AND metrics.deleted_at ISNULL AND (user_id = %(user_id)s OR metrics.is_public) @@ -246,7 +252,7 @@ def delete(project_id, metric_id, user_id): cur.execute( cur.mogrify("""\ UPDATE public.metrics - SET deleted_at = timezone('utc'::text, now()) + SET deleted_at = timezone('utc'::text, now()), edited_at = timezone('utc'::text, now()) WHERE project_id = %(project_id)s AND metric_id = %(metric_id)s AND (user_id = %(user_id)s OR is_public);""", @@ -274,6 +280,11 @@ def get(metric_id, project_id, user_id, flatten=True): AND project_id = %(project_id)s AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards ) AS connected_dashboards ON (TRUE) + LEFT JOIN LATERAL (SELECT email AS owner_email + FROM users + WHERE deleted_at ISNULL + AND users.user_id = metrics.user_id + ) AS owner ON (TRUE) WHERE metrics.project_id = %(project_id)s AND metrics.deleted_at ISNULL AND (metrics.user_id = %(user_id)s OR metrics.is_public) diff --git a/api/schemas.py b/api/schemas.py index b707a53e1..a7fd003b7 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -776,6 +776,7 @@ class CustomMetricCreateSeriesSchema(BaseModel): class MetricTimeseriesViewType(str, Enum): line_chart = "lineChart" progress = "progress" + area_chart = "areaChart" class MetricTableViewType(str, Enum): diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 3080f8dbb..d4d8575ae 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -18,50 +18,23 @@ CREATE TABLE IF NOT EXISTS dashboards deleted_at timestamp NULL DEFAULT NULL ); --- CREATE TABLE templates --- ( --- template_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, --- template_key text, --- name text NOT NULL, --- category text NOT NULL, --- series jsonb NOT NULL, --- config jsonb NOT NULL, --- predefined boolean DEFAULT TRUE --- ); - --- CREATE TABLE dashboard_widgets --- ( --- widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, --- dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, --- metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, --- -- template_id integer NOT NULL REFERENCES templates (template_id) ON DELETE CASCADE, --- user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, --- created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), --- configuration jsonb NOT NULL DEFAULT '{}'::jsonb, --- name text --- ); - --- INSERT INTO public.templates (name, category, series, config, predefined, template_key) --- VALUES ('captured sessions', 'overview', '[]', '{}', true, 'count_sessions'), --- ('request load time', 'overview', '[]', '{}', true, 'avg_request_load_time'), --- ('page load time', 'overview', '[]', '{}', true, 'avg_page_load_time'), --- ('image load time', 'overview', '[]', '{}', true, 'avg_image_load_time'); - -ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; ALTER TABLE IF EXISTS metrics - DROP CONSTRAINT IF EXISTS null_project_id_for_template_only; + DROP CONSTRAINT IF EXISTS null_project_id_for_template_only, + DROP CONSTRAINT IF EXISTS unique_key; ALTER TABLE IF EXISTS metrics - ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', - ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS key text NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, + ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', + ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS key text NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, ALTER COLUMN project_id DROP NOT NULL, ADD CONSTRAINT null_project_id_for_template_only - CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ); + CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), + ADD CONSTRAINT unique_key UNIQUE (key); @@ -76,17 +49,33 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets name text ); --- INSERT INTO public.templates (name, category, series, config, predefined, template_key) --- VALUES ('captured sessions', 'overview', '[]', '{}', true, 'count_sessions'), --- ('request load time', 'overview', '[]', '{}', true, 'avg_request_load_time'), --- ('page load time', 'overview', '[]', '{}', true, 'avg_page_load_time'), --- ('image load time', 'overview', '[]', '{}', true, 'avg_image_load_time'); INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) -VALUES ('captured sessions', 'overview', '{}', true, true, true, 'count_sessions'), - ('request load time', 'overview', '{}', true, true, true, 'avg_request_load_time'), - ('page load time', 'overview', '{}', true, true, true, 'avg_page_load_time'), - ('image load time', 'overview', '{}', true, true, true, 'avg_image_load_time'); +VALUES ('sessions count', 'overview', '{}', true, true, true, 'count_sessions'), + ('avg request load time', 'overview', '{}', true, true, true, 'avg_request_load_time'), + ('avg page load time', 'overview', '{}', true, true, true, 'avg_page_load_time'), + ('avg image load time', 'overview', '{}', true, true, true, 'avg_image_load_time'), + ('avg dom content load start', 'overview', '{}', true, true, true, 'avg_dom_content_load_start'), + ('avg first contentful pixel', 'overview', '{}', true, true, true, 'avg_first_contentful_pixel'), + ('avg visited pages count', 'overview', '{}', true, true, true, 'avg_visited_pages'), + ('avg session duration', 'overview', '{}', true, true, true, 'avg_session_duration'), + ('avg pages dom buildtime', 'overview', '{}', true, true, true, 'avg_pages_dom_buildtime'), + ('avg pages response time', 'overview', '{}', true, true, true, 'avg_pages_response_time'), + ('avg response time', 'overview', '{}', true, true, true, 'avg_response_time'), + ('avg first paint', 'overview', '{}', true, true, true, 'avg_first_paint'), + ('avg dom content loaded', 'overview', '{}', true, true, true, 'avg_dom_content_loaded'), + ('avg time till first bit', 'overview', '{}', true, true, true, 'avg_till_first_bit'), + ('avg time to interactive', 'overview', '{}', true, true, true, 'avg_time_to_interactive'), + ('requests count', 'overview', '{}', true, true, true, 'count_requests'), + ('avg time to render', 'overview', '{}', true, true, true, 'avg_time_to_render'), + ('avg used js heap size', 'overview', '{}', true, true, true, 'avg_used_js_heap_size'), + ('avg cpu', 'overview', '{}', true, true, true, 'avg_cpu') +ON CONFLICT (key) DO UPDATE SET name=excluded.name, + category=excluded.category, + config=excluded.config, + is_predefined=excluded.is_predefined, + is_template=excluded.is_template, + is_public=excluded.is_public; - -COMMIT \ No newline at end of file +COMMIT; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; \ No newline at end of file diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 35926bc96..c6f628e32 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -947,6 +947,7 @@ $$ active boolean NOT NULL DEFAULT TRUE, created_at timestamp default timezone('utc'::text, now()) not null, deleted_at timestamp, + edited_at timestamp, metric_type metric_type NOT NULL DEFAULT 'timeseries', view_type metric_view_type NOT NULL DEFAULT 'lineChart', metric_of text NOT NULL DEFAULT 'sessionCount', From 92fb75b228ae5e6cf32c635bb10a8c9c61a341c9 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 14:06:30 +0200 Subject: [PATCH 42/87] feat(db): include default metrics in init_schema.sql feat(api): refactored overview --- api/build_local.sh | 3 --- api/routers/subs/dashboard.py | 6 +++-- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 2 +- .../db/init_dbs/postgresql/init_schema.sql | 27 +++++++++++++++++++ 4 files changed, 32 insertions(+), 6 deletions(-) delete mode 100644 api/build_local.sh diff --git a/api/build_local.sh b/api/build_local.sh deleted file mode 100644 index 4253fd812..000000000 --- a/api/build_local.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -f ./Dockerfile --build-arg envarg="default-foss" -t chalice:local . \ No newline at end of file diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index fdb5d641a..0832be4b8 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -388,8 +388,10 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, {"key": schemas.TemplateKeys.avg_used_js_heap_size, "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_cpu, "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_fps, "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + {"key": schemas.TemplateKeys.avg_cpu, + "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, + {"key": schemas.TemplateKeys.avg_fps, + "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} ] results = sorted(results, key=lambda r: r["key"]) return {"data": results} diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index d4d8575ae..6c563bfdc 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -59,7 +59,7 @@ VALUES ('sessions count', 'overview', '{}', true, true, true, 'count_sessions'), ('avg first contentful pixel', 'overview', '{}', true, true, true, 'avg_first_contentful_pixel'), ('avg visited pages count', 'overview', '{}', true, true, true, 'avg_visited_pages'), ('avg session duration', 'overview', '{}', true, true, true, 'avg_session_duration'), - ('avg pages dom buildtime', 'overview', '{}', true, true, true, 'avg_pages_dom_buildtime'), + ('avg pages dom build time', 'overview', '{}', true, true, true, 'avg_pages_dom_buildtime'), ('avg pages response time', 'overview', '{}', true, true, true, 'avg_pages_response_time'), ('avg response time', 'overview', '{}', true, true, true, 'avg_response_time'), ('avg first paint', 'overview', '{}', true, true, true, 'avg_first_paint'), diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index c6f628e32..5b5bd7044 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1025,4 +1025,31 @@ $$ $$ LANGUAGE plpgsql; +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) +VALUES ('sessions count', 'overview', '{}', true, true, true, 'count_sessions'), + ('avg request load time', 'overview', '{}', true, true, true, 'avg_request_load_time'), + ('avg page load time', 'overview', '{}', true, true, true, 'avg_page_load_time'), + ('avg image load time', 'overview', '{}', true, true, true, 'avg_image_load_time'), + ('avg dom content load start', 'overview', '{}', true, true, true, 'avg_dom_content_load_start'), + ('avg first contentful pixel', 'overview', '{}', true, true, true, 'avg_first_contentful_pixel'), + ('avg visited pages count', 'overview', '{}', true, true, true, 'avg_visited_pages'), + ('avg session duration', 'overview', '{}', true, true, true, 'avg_session_duration'), + ('avg pages dom build time', 'overview', '{}', true, true, true, 'avg_pages_dom_buildtime'), + ('avg pages response time', 'overview', '{}', true, true, true, 'avg_pages_response_time'), + ('avg response time', 'overview', '{}', true, true, true, 'avg_response_time'), + ('avg first paint', 'overview', '{}', true, true, true, 'avg_first_paint'), + ('avg dom content loaded', 'overview', '{}', true, true, true, 'avg_dom_content_loaded'), + ('avg time till first bit', 'overview', '{}', true, true, true, 'avg_till_first_bit'), + ('avg time to interactive', 'overview', '{}', true, true, true, 'avg_time_to_interactive'), + ('requests count', 'overview', '{}', true, true, true, 'count_requests'), + ('avg time to render', 'overview', '{}', true, true, true, 'avg_time_to_render'), + ('avg used js heap size', 'overview', '{}', true, true, true, 'avg_used_js_heap_size'), + ('avg cpu', 'overview', '{}', true, true, true, 'avg_cpu') +ON CONFLICT (key) DO UPDATE SET name=excluded.name, + category=excluded.category, + config=excluded.config, + is_predefined=excluded.is_predefined, + is_template=excluded.is_template, + is_public=excluded.is_public; + COMMIT; \ No newline at end of file From f4f543ec60045002114eab9910e1a0f43b095586 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 5 Apr 2022 17:00:29 +0200 Subject: [PATCH 43/87] feat(ui) - dashboard - wip --- frontend/app/Router.js | 5 + .../app/components/Dashboard/NewDashboard.tsx | 6 +- .../DashbaordListModal/DashbaordListModal.tsx | 45 ++- .../DashboardMetricSelection.tsx | 58 +-- .../DashboardModal/DashboardModal.tsx | 31 +- .../DashboardRouter/DashboardRouter.tsx | 7 +- .../DashboardSideMenu/DashboardSideMenu.tsx | 4 +- .../DashboardView/DashboardView.tsx | 73 +++- .../components/FilterSeries/FilterSeries.tsx | 10 +- .../MetricListItem/MetricListItem.tsx | 17 +- .../components/MetricsList/MetricsList.tsx | 2 +- .../components/WidgetChart/WidgetChart.tsx | 103 ++++- .../components/WidgetForm/WidgetForm.tsx | 42 +- .../WidgetPreview/WidgetPreview.tsx | 2 +- .../WidgetWrapper/WidgetWrapper.tsx | 14 +- .../components/Dashboard/store/dashboard.ts | 152 -------- .../Dashboard/store/dashboardStore.ts | 362 ------------------ .../app/components/Dashboard/store/filter.ts | 37 -- .../Dashboard/store/filterSeries.ts | 22 -- .../app/components/Dashboard/store/index.ts | 1 - .../app/components/Dashboard/store/widget.ts | 127 ------ .../shared/Filters/FilterList/FilterList.tsx | 7 +- .../Filters/FilterValue/FilterValue.tsx | 2 +- frontend/app/initialize.js | 6 +- frontend/app/mstore/dashboardStore.ts | 163 +++++--- frontend/app/mstore/metricStore.ts | 19 +- frontend/app/mstore/types/dashboard.ts | 13 +- frontend/app/mstore/types/filter.ts | 5 +- frontend/app/mstore/types/filterItem.ts | 5 + frontend/app/mstore/types/widget.ts | 22 +- frontend/app/services/DashboardService.ts | 4 +- frontend/app/services/MetricService.ts | 8 + 32 files changed, 471 insertions(+), 903 deletions(-) delete mode 100644 frontend/app/components/Dashboard/store/dashboard.ts delete mode 100644 frontend/app/components/Dashboard/store/dashboardStore.ts delete mode 100644 frontend/app/components/Dashboard/store/filter.ts delete mode 100644 frontend/app/components/Dashboard/store/filterSeries.ts delete mode 100644 frontend/app/components/Dashboard/store/index.ts delete mode 100644 frontend/app/components/Dashboard/store/widget.ts diff --git a/frontend/app/Router.js b/frontend/app/Router.js index a31391127..22b5b3947 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -36,6 +36,8 @@ import { OB_DEFAULT_TAB } from 'App/routes'; import Signup from './components/Signup/Signup'; import { fetchTenants } from 'Duck/user'; import { setSessionPath } from 'Duck/sessions'; +import { ModalProvider } from './components/Modal'; +import ModalRoot from './components/Modal/ModalRoot'; const BugFinder = withSiteIdUpdater(BugFinderPure); const Dashboard = withSiteIdUpdater(DashboardPure); @@ -169,6 +171,8 @@ class Router extends React.Component { }> + + @@ -225,6 +229,7 @@ class Router extends React.Component { )) } + : diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 56395f8a9..f66dbe4de 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -18,12 +18,12 @@ function NewDashboard(props) { useEffect(() => { dashboardStore.fetchList().then((resp) => { - if (dashboardId) { + if (parseInt(dashboardId) > 0) { dashboardStore.selectDashboardById(dashboardId); } else { - dashboardStore.selectDefaultDashboard().then((b) => { + dashboardStore.selectDefaultDashboard().then(({ dashboardId }) => { if (!history.location.pathname.includes('/metrics')) { - history.push(withSiteId(dashboardSelected(b.dashboardId), siteId)); + history.push(withSiteId(dashboardSelected(dashboardId), siteId)); } }); } diff --git a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx index 8a5892cb3..d91d058b0 100644 --- a/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx +++ b/frontend/app/components/Dashboard/components/DashbaordListModal/DashbaordListModal.tsx @@ -1,30 +1,45 @@ import React from 'react'; import { useStore } from 'App/mstore'; import { SideMenuitem, SideMenuHeader, Icon, Button } from 'UI'; +import { withSiteId, dashboardSelected, metrics } from 'App/routes'; +import { withRouter } from 'react-router-dom'; +import { useModal } from 'App/components/Modal'; -function DashbaordListModal(props) { +interface Props { + siteId: string + history: any +} +function DashbaordListModal(props: Props) { const { dashboardStore } = useStore(); + const { hideModal } = useModal(); const dashboards = dashboardStore.dashboards; const activeDashboardId = dashboardStore.selectedDashboard?.dashboardId; + + const onItemClick = (dashboard) => { + dashboardStore.selectDashboardById(dashboard.dashboardId); + const path = withSiteId(dashboardSelected(dashboard.dashboardId), parseInt(props.siteId)); + props.history.push(path); + hideModal(); + }; return (
Dashboards
{dashboards.map((item: any) => (
- onItemClick(item)} // TODO add click handler - leading = {( -
- {item.isPublic &&
} - {item.isPinned &&
} -
- )} - /> + onItemClick(item)} // TODO add click handler + leading = {( +
+ {item.isPublic &&
} + {item.isPinned &&
} +
+ )} + />
))}
@@ -32,4 +47,4 @@ function DashbaordListModal(props) { ); } -export default DashbaordListModal; \ No newline at end of file +export default withRouter(DashbaordListModal); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index 90729935f..78f50a45e 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import WidgetWrapper from '../WidgetWrapper'; import { useObserver } from 'mobx-react-lite'; import cn from 'classnames'; @@ -6,14 +6,14 @@ import { useStore } from 'App/mstore'; function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, unSelectCategory }) { const selectedCategoryWidgetsCount = useObserver(() => { - return category.widgets.filter(widget => selectedWidgetIds.includes(widget.widgetId)).length; + return category.widgets.filter(widget => selectedWidgetIds.includes(widget.metricId)).length; }); return (
onClick(category)} > -
{category.name}
+
{category.name}
{category.description}
{selectedCategoryWidgetsCount > 0 && (
@@ -27,40 +27,22 @@ function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, function DashboardMetricSelection(props) { const { dashboardStore } = useStore(); - const widgetCategories = dashboardStore?.widgetCategories; - const [activeCategory, setActiveCategory] = React.useState(widgetCategories[0]); - const [selectedWidgets, setSelectedWidgets] = React.useState([]); - const selectedWidgetIds = selectedWidgets.map((widget: any) => widget.widgetId); + let widgetCategories: any[] = useObserver(() => dashboardStore.widgetCategories); + const [activeCategory, setActiveCategory] = React.useState(); + const selectedWidgetIds = useObserver(() => dashboardStore.selectedWidgets.map((widget: any) => widget.metricId)); - const removeSelectedWidgetByCategory = (category: any) => { - const categoryWidgetIds = category.widgets.map((widget: any) => widget.widgetId); - const newSelectedWidgets = selectedWidgets.filter((widget: any) => !categoryWidgetIds.includes(widget.widgetId)); - setSelectedWidgets(newSelectedWidgets); - }; - - const toggleWidgetSelection = (widget: any) => { - console.log('toggleWidgetSelection', widget.widgetId); - if (selectedWidgetIds.includes(widget.widgetId)) { - setSelectedWidgets(selectedWidgets.filter((w: any) => w.widgetId !== widget.widgetId)); - } else { - setSelectedWidgets(selectedWidgets.concat(widget)); - } - }; + useEffect(() => { + dashboardStore?.fetchTemplates().then(templates => { + setActiveCategory(dashboardStore.widgetCategories[0]); + }); + }, []); const handleWidgetCategoryClick = (category: any) => { setActiveCategory(category); }; const toggleAllWidgets = ({ target: { checked }}) => { - if (checked == true) { - const allWidgets = widgetCategories.reduce((acc, category) => { - return acc.concat(category.widgets); - }, []); - - setSelectedWidgets(allWidgets); - } else { - setSelectedWidgets([]); - } + dashboardStore.toggleAllSelectedWidgets(checked); } return useObserver(() => ( @@ -74,7 +56,7 @@ function DashboardMetricSelection(props) { {activeCategory && ( <>
-

{activeCategory.name}

+

{activeCategory.name}

{activeCategory.widgets.length}
@@ -92,14 +74,14 @@ function DashboardMetricSelection(props) {
- {widgetCategories.map((category, index) => + {activeCategory && widgetCategories.map((category, index) => )}
@@ -108,9 +90,9 @@ function DashboardMetricSelection(props) {
{activeCategory && activeCategory.widgets.map((widget: any) => (
toggleWidgetSelection(widget)} + key={widget.metricId} + className={cn("border rounded cursor-pointer", { 'border-color-teal' : selectedWidgetIds.includes(widget.metricId) })} + onClick={() => dashboardStore.toggleWidgetSelection(widget)} >
diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 4e3a014c8..8c102763b 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -10,11 +10,15 @@ import { useModal } from 'App/components/Modal'; function DashboardModal(props) { const { dashboardStore } = useStore(); const { hideModal } = useModal(); - const dashbaord = useObserver(() => dashboardStore.dashboardInstance); + const dashboard = useObserver(() => dashboardStore.dashboardInstance); const loading = useObserver(() => dashboardStore.isSaving); const onSave = () => { - dashboardStore.save(dashbaord).then(hideModal) + dashboardStore.save(dashboard).then(hideModal) + } + + const handleCreateNew = () => { + hideModal(); } return useObserver(() => ( @@ -22,21 +26,32 @@ function DashboardModal(props) { className="fixed border-r shadow p-4 h-screen" style={{ backgroundColor: '#FAFAFA', zIndex: '9999', width: '80%'}} > -
-

Create Dashboard

+
+
+

+ { dashboard.exists() ? "Add metric(s) to dashboard" : "Create Dashboard" } +

+
+
+ +
- -

Create new dashboard by choosing from the range of predefined metrics that you care about. You can always add your custom metrics later.

+ { !dashboard.exists() && ( + <> + +

Create new dashboard by choosing from the range of predefined metrics that you care about. You can always add your custom metrics later.

+ + )}
diff --git a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx index 82ab00a0e..171a36164 100644 --- a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx +++ b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx @@ -8,6 +8,7 @@ import { dashboardSelected, dashboardMetricCreate, withSiteId, + dashboard, } from 'App/routes'; import DashboardView from '../DashboardView'; import MetricsView from '../MetricsView'; @@ -34,8 +35,12 @@ function DashboardRouter(props: Props) { + + <>Nothing... + + - +
diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index c53a7a378..087ae7e07 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -18,7 +18,7 @@ function DashboardSideMenu(props: Props) { const { hideModal, showModal } = useModal(); const { dashboardStore } = useStore(); const dashboardId = dashboardStore.selectedDashboard?.dashboardId; - const dashboardsPicked = dashboardStore.dashboards.slice(0, SHOW_COUNT); + const dashboardsPicked = useObserver(() => dashboardStore.dashboards.slice(0, SHOW_COUNT)); const remainingDashboardsCount = dashboardStore.dashboards.length - SHOW_COUNT; const redirect = (path) => { @@ -58,7 +58,7 @@ function DashboardSideMenu(props: Props) { {remainingDashboardsCount > 0 && (
showModal(, {})} + onClick={() => showModal(, {})} > {remainingDashboardsCount} More
diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index 4ac3380c6..3c0f96a92 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -1,33 +1,74 @@ -import React from 'react'; -import { observer } from 'mobx-react-lite'; +import React, { useEffect } from 'react'; +import { observer, useObserver } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; -import { Button, PageTitle, Link } from 'UI'; -import { withSiteId, dashboardMetricCreate } from 'App/routes'; +import { Button, PageTitle, Link, Loader, NoContent } from 'UI'; +import { withSiteId, dashboardMetricCreate, dashboardSelected, dashboard } from 'App/routes'; import withModal from 'App/components/Modal/withModal'; import DashboardWidgetGrid from '../DashboardWidgetGrid'; +import { confirm } from 'UI/Confirmation'; +import { withRouter } from 'react-router-dom'; +import { useModal } from 'App/components/Modal'; +import DashboardModal from '../DashboardModal'; interface Props { siteId: number; + history: any + match: any + dashboardId: any } function DashboardView(props: Props) { - const { siteId } = props; + const { siteId, dashboardId } = props; const { dashboardStore } = useStore(); + const { hideModal, showModal } = useModal(); + const loading = useObserver(() => dashboardStore.fetchingDashboard); const dashboard: any = dashboardStore.selectedDashboard + + useEffect(() => { + dashboardStore.fetch(dashboardId) + }, []); + + const onEditHandler = () => { + dashboardStore.initDashboard(dashboard) + showModal(, {}) + } + + const onDelete = async () => { + if (await confirm({ + header: 'Confirm', + confirmButton: 'Yes, Delete', + confirmation: `Are you sure you want to permanently delete this Dashboard?` + })) { + dashboardStore.deleteDashboard(dashboard).then(() => { + dashboardStore.selectDefaultDashboard().then(({ dashboardId }) => { + props.history.push(withSiteId(dashboard(), siteId)); + }); + }); + } + } return ( -
-
-
- - -
+ +
- Right +
+
+ + {/* */} + +
+
+ +
+
+
-
- -
+ + ) } -export default withModal(observer(DashboardView)); \ No newline at end of file +export default withRouter(withModal(observer(DashboardView))); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx index c43e1b97a..337aa1a8a 100644 --- a/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx +++ b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import FilterList from 'Shared/Filters/FilterList'; import { edit, @@ -29,13 +29,16 @@ interface Props { removeSeriesFilterFilter: typeof removeSeriesFilterFilter; hideHeader?: boolean; emptyMessage?: any; + observeChanges?: () => void; } function FilterSeries(props: Props) { - const { canDelete, hideHeader = false, emptyMessage = 'Add user event or filter to define the series by clicking Add Step.' } = props; + const { observeChanges = () => {}, canDelete, hideHeader = false, emptyMessage = 'Add user event or filter to define the series by clicking Add Step.' } = props; const [expanded, setExpanded] = useState(true) const { series, seriesIndex } = props; + useEffect(observeChanges, [series]) + const onAddFilter = (filter) => { series.filter.addFilter(filter) } @@ -58,7 +61,7 @@ function FilterSeries(props: Props) {
- props.updateSeries(seriesIndex, { name }) } /> + series.update('name', name) } />
@@ -80,6 +83,7 @@ function FilterSeries(props: Props) { onUpdateFilter={onUpdateFilter} onRemoveFilter={onRemoveFilter} onChangeEventsOrder={onChangeEventsOrder} + observeChanges={observeChanges} /> ): (
{emptyMessage}
diff --git a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx index cafcf53d8..b8e623370 100644 --- a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx +++ b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Icon, NoContent, Label, Link, Pagination } from 'UI'; +import { checkForRecent, formatDateTimeDefault, convertTimestampToUtcTimestamp } from 'App/date'; interface Props { metric: any; @@ -30,17 +31,15 @@ function MetricListItem(props: Props) {
- {/*
-
·
- Dashboards -
*/}
{metric.owner}
- {/*
- - {metric.isPublic ? 'Team' : 'Private'} -
*/} -
Last Modified
+
+
+ + {metric.isPublic ? 'Team' : 'Private'} +
+
+
{metric.lastModified && checkForRecent(metric.lastModified, 'LLL dd, yyyy, hh:mm a')}
); } diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index 9ef809261..729844590 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -24,7 +24,7 @@ function MetricsList(props: Props) {
Type
Dashboards
Owner
- {/*
Visibility & Edit Access
*/} +
Visibility & Edit Access
Last Modified
diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index 53f15faf5..2dd27bc9e 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -1,27 +1,110 @@ -import React from 'react'; - +import React, { useState, useRef, useEffect } from 'react'; +import Period, { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period'; +import CustomMetriLineChart from '../../Widgets/CustomMetricsWidgets/CustomMetriLineChart'; +import CustomMetricPercentage from '../../Widgets/CustomMetricsWidgets/CustomMetricPercentage'; +import CustomMetricTable from '../../Widgets/CustomMetricsWidgets/CustomMetricTable'; +import CustomMetricPieChart from '../../Widgets/CustomMetricsWidgets/CustomMetricPieChart'; +import APIClient from 'App/api_client'; +import { Styles } from '../../Widgets/common'; +import { getChartFormatter } from 'Types/dashboard/helper'; +import { observer, useObserver } from 'mobx-react-lite'; +import { Loader } from 'UI'; +import { useStore } from 'App/mstore'; interface Props { - metric: any; + // metric: any; } function WidgetChart(props: Props) { - const { metric } = props; + // const metric = useObserver(() => props.metric); + const { metricStore } = useStore(); + const metric: any = useObserver(() => metricStore.instance); + const series = useObserver(() => metric.series); + const colors = Styles.customMetricColors; + const [loading, setLoading] = useState(false) + const [data, setData] = useState({ chart: [{}] }) + const [seriesMap, setSeriesMap] = useState([]); + const [period, setPeriod] = useState(Period({ rangeName: metric.rangeName, startDate: metric.startDate, endDate: metric.endDate })); + const params = { density: 70 } + const metricParams = { ...params, metricId: metric.metricId, viewType: 'lineChart' } + const prevMetricRef = useRef(); + + useEffect(() => { + // Check for title change + if (prevMetricRef.current && prevMetricRef.current.name !== metric.name) { + prevMetricRef.current = metric; + return + }; + prevMetricRef.current = metric; + setLoading(true); + + // fetch new data for the widget preview + new APIClient()['post']('/custom_metrics/try', { ...metricParams, ...metric.toJson() }) + .then(response => response.json()) + .then(({ errors, data }) => { + if (errors) { + console.log('err', errors) + } else { + const namesMap = data + .map(i => Object.keys(i)) + .flat() + .filter(i => i !== 'time' && i !== 'timestamp') + .reduce((unique: any, item: any) => { + if (!unique.includes(item)) { + unique.push(item); + } + return unique; + }, []); + + setSeriesMap(namesMap); + setData(getChartFormatter(period)(data)); + } + }).finally(() => setLoading(false)); + }, [metric.data]); + const renderChart = () => { - const { metricType } = metric; + const { metricType, viewType } = metric; if (metricType === 'timeseries') { - return
Chart
; + if (viewType === 'lineChart') { + return ( + + ) + } else if (viewType === 'progress') { + return ( + + ) + } } if (metricType === 'table') { - return
Table
; + if (viewType === 'table') { + return ; + } else if (viewType === 'pieChart') { + return ( + + ) + } } return
Unknown
; } return ( -
+ {renderChart()} -
+ ); } -export default WidgetChart; \ No newline at end of file +export default observer(WidgetChart); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 373c399c9..05a2ec910 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -17,9 +17,8 @@ interface Props { function WidgetForm(props: Props) { const { history, match: { params: { siteId, dashboardId, metricId } } } = props; - console.log('WidgetForm params', props.match.params); const { metricStore } = useStore(); - const metric: any = metricStore.instance; + const metric: any = useObserver(() => metricStore.instance); const timeseriesOptions = metricOf.filter(i => i.type === 'timeseries'); const tableOptions = metricOf.filter(i => i.type === 'table'); @@ -31,23 +30,23 @@ function WidgetForm(props: Props) { const writeOption = (e, { value, name }) => { metricStore.merge({ [ name ]: value }); - if (name === 'metricValue') { - metricStore.merge({ metricValue: [value] }); - } - - if (name === 'metricOf') { - if (value === FilterKey.ISSUE) { - metricStore.merge({ metricValue: ['all'] }); + if (name === 'metricValue') { + metricStore.merge({ metricValue: [value] }); } - } - - if (name === 'metricType') { - if (value === 'timeseries') { - metricStore.merge({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }); - } else if (value === 'table') { - metricStore.merge({ metricOf: tableOptions[0].value, viewType: 'table' }); + + if (name === 'metricOf') { + if (value === FilterKey.ISSUE) { + metricStore.merge({ metricValue: ['all'] }); + } + } + + if (name === 'metricType') { + if (value === 'timeseries') { + metricStore.merge({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }); + } else if (value === 'table') { + metricStore.merge({ metricOf: tableOptions[0].value, viewType: 'table' }); + } } - } }; const onSave = () => { @@ -61,11 +60,13 @@ function WidgetForm(props: Props) { confirmation: `Are you sure you want to permanently delete this metric?` })) { metricStore.delete(metric).then(props.onDelete); - // props.remove(instance.alertId).then(() => { - // toggleForm(null, false); - // }); } } + + const onObserveChanges = () => { + console.log('observe changes'); + // metricStore.fetchMetricChartData(metric); + } return useObserver(() => (
@@ -156,6 +157,7 @@ function WidgetForm(props: Props) { 'Filter data using any event or attribute. Use Add Step button below to do so.' : 'Add user event or filter to define the series by clicking Add Step.' } + observeChanges={onObserveChanges} />
))} diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index 11b9e326c..a227fce93 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -12,7 +12,7 @@ interface Props { function WidgetPreview(props: Props) { const { className = '' } = props; const { metricStore } = useStore(); - const metric: any = metricStore.instance; + const metric: any = useObserver(() => metricStore.instance); const isTimeSeries = metric.metricType === 'timeseries'; const isTable = metric.metricType === 'table'; diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 92c3f42fd..6a43577a2 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -1,8 +1,9 @@ -import React, { useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; import cn from 'classnames'; import { ItemMenu } from 'UI'; import { useDrag, useDrop } from 'react-dnd'; import WidgetChart from '../WidgetChart'; +import { useObserver } from 'mobx-react-lite'; interface Props { className?: string; @@ -12,7 +13,7 @@ interface Props { isPreview?: boolean; } function WidgetWrapper(props: Props) { - const { widget = {}, index = 0, moveListItem = null, isPreview = false } = props; + const { widget, index = 0, moveListItem = null, isPreview = false } = props; const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -21,7 +22,6 @@ function WidgetWrapper(props: Props) { isDragging: monitor.isDragging(), opacity: monitor.isDragging() ? 0.5 : 1, }), - }); const [{ isOver, canDrop }, dropRef] = useDrop({ @@ -40,7 +40,7 @@ function WidgetWrapper(props: Props) { const ref: any = useRef(null) const dragDropRef: any = dragRef(dropRef(ref)) - return ( + return useObserver(() => (
-
- +
+
{/* */}
- ); + )); } export default WidgetWrapper; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/dashboard.ts b/frontend/app/components/Dashboard/store/dashboard.ts deleted file mode 100644 index dd253a6d3..000000000 --- a/frontend/app/components/Dashboard/store/dashboard.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { makeAutoObservable, observable, action, runInAction } from "mobx" -import Widget, { IWidget } from "./widget" -import { dashboardService } from 'App/services' - -export interface IDashboard { - dashboardId: any - name: string - isPublic: boolean - widgets: IWidget[] - isValid: boolean - isPinned: boolean - currentWidget: IWidget - - update(data: any): void - toJson(): any - fromJson(json: any): void - validate(): void - addWidget(widget: IWidget): void - removeWidget(widgetId: string): void - updateWidget(widget: IWidget): void - getWidget(widgetId: string): void - getWidgetIndex(widgetId: string) - getWidgetByIndex(index: number): void - getWidgetCount(): void - getWidgetIndexByWidgetId(widgetId: string): void - swapWidgetPosition(positionA: number, positionB: number): void - sortWidgets(): void -} -export default class Dashboard implements IDashboard { - dashboardId: any = undefined - name: string = "New Dashboard" - isPublic: boolean = false - widgets: IWidget[] = [] - isValid: boolean = false - isPinned: boolean = false - currentWidget: IWidget = new Widget() - - constructor() { - makeAutoObservable(this, { - name: observable, - isPublic: observable, - widgets: observable, - isValid: observable, - - toJson: action, - fromJson: action, - addWidget: action, - removeWidget: action, - updateWidget: action, - getWidget: action, - getWidgetIndex: action, - getWidgetByIndex: action, - getWidgetCount: action, - getWidgetIndexByWidgetId: action, - validate: action, - sortWidgets: action, - swapWidgetPosition: action, - update: action, - }) - - this.validate(); - } - - update(data: any) { - runInAction(() => { - Object.assign(this, data) - }) - this.validate() - } - - toJson() { - return { - dashboardId: this.dashboardId, - name: this.name, - isPrivate: this.isPublic, - widgets: this.widgets.map(w => w.toJson()) - } - } - - fromJson(json: any) { - runInAction(() => { - this.dashboardId = json.dashboardId - this.name = json.name - this.isPublic = json.isPublic - this.isPinned = json.isPinned - this.widgets = json.widgets ? json.widgets.map(w => new Widget().fromJson(w)) : [] - }) - return this - } - - validate() { - return this.isValid = this.name.length > 0 - } - - addWidget(widget: IWidget) { - this.widgets.push(widget) - } - - removeWidget(widgetId: string) { - this.widgets = this.widgets.filter(w => w.widgetId !== widgetId) - } - - updateWidget(widget: IWidget) { - const index = this.widgets.findIndex(w => w.widgetId === widget.widgetId) - if (index >= 0) { - this.widgets[index] = widget - } - } - - getWidget(widgetId: string) { - return this.widgets.find(w => w.widgetId === widgetId) - } - - getWidgetIndex(widgetId: string) { - return this.widgets.findIndex(w => w.widgetId === widgetId) - } - - getWidgetByIndex(index: number) { - return this.widgets[index] - } - - getWidgetCount() { - return this.widgets.length - } - - getWidgetIndexByWidgetId(widgetId: string) { - return this.widgets.findIndex(w => w.widgetId === widgetId) - } - - swapWidgetPosition(positionA, positionB) { - console.log('swapWidgetPosition', positionA, positionB) - const widgetA = this.widgets[positionA] - const widgetB = this.widgets[positionB] - this.widgets[positionA] = widgetB - this.widgets[positionB] = widgetA - - widgetA.position = positionB - widgetB.position = positionA - } - - sortWidgets() { - this.widgets = this.widgets.sort((a, b) => { - if (a.position > b.position) { - return 1 - } else if (a.position < b.position) { - return -1 - } else { - return 0 - } - }) - } -} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/dashboardStore.ts b/frontend/app/components/Dashboard/store/dashboardStore.ts deleted file mode 100644 index 65dbf861b..000000000 --- a/frontend/app/components/Dashboard/store/dashboardStore.ts +++ /dev/null @@ -1,362 +0,0 @@ -import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" -import Dashboard, { IDashboard } from "./dashboard" -import APIClient from 'App/api_client'; -import Widget, { IWidget } from "./widget"; -import { dashboardService } from "App/services"; - -export interface IDashboardSotre { - dashboards: IDashboard[] - widgetTemplates: any[] - selectedDashboard: IDashboard | null - dashboardInstance: IDashboard - siteId: any - currentWidget: Widget - widgetCategories: any[] - widgets: Widget[] - metricsPage: number - metricsPageSize: number - metricsSearch: string - - isLoading: boolean - isSaving: boolean - - initDashboard(dashboard?: IDashboard): void - updateKey(key: string, value: any): void - resetCurrentWidget(): void - editWidget(widget: any): void - fetchList(): void - fetch(dashboardId: string) - save(dashboard: IDashboard): Promise - saveDashboardWidget(dashboard: Dashboard, widget: Widget) - delete(dashboard: IDashboard) - toJson(): void - fromJson(json: any): void - initDashboard(dashboard: IDashboard): void - addDashboard(dashboard: IDashboard): void - removeDashboard(dashboard: IDashboard): void - getDashboard(dashboardId: string): void - getDashboardIndex(dashboardId: string): number - getDashboardCount(): void - getDashboardIndexByDashboardId(dashboardId: string): number - updateDashboard(dashboard: IDashboard): void - selectDashboardById(dashboardId: string): void - setSiteId(siteId: any): void - selectDefaultDashboard(): Promise - - saveMetric(metric: IWidget, dashboardId?: string): Promise -} -export default class DashboardStore implements IDashboardSotre { - siteId: any = null - // Dashbaord / Widgets - dashboards: Dashboard[] = [] - widgetTemplates: any[] = [] - selectedDashboard: Dashboard | null = new Dashboard() - dashboardInstance: IDashboard = new Dashboard() - currentWidget: Widget = new Widget() - widgetCategories: any[] = [] - widgets: Widget[] = [] - - // Metrics - metricsPage: number = 1 - metricsPageSize: number = 10 - metricsSearch: string = '' - - // Loading states - isLoading: boolean = false - isSaving: boolean = false - - private client = new APIClient() - - constructor() { - makeAutoObservable(this, { - resetCurrentWidget: action, - addDashboard: action, - removeDashboard: action, - updateDashboard: action, - getDashboard: action, - getDashboardIndex: action, - getDashboardByIndex: action, - getDashboardCount: action, - getDashboardIndexByDashboardId: action, - selectDashboardById: action, - selectDefaultDashboard: action, - toJson: action, - fromJson: action, - setSiteId: action, - editWidget: action, - updateKey: action, - }) - - - // TODO remove this sample data - // this.dashboards = sampleDashboards - - // for (let i = 0; i < 15; i++) { - // const widget: any= {}; - // widget.widgetId = `${i}` - // widget.name = `Widget ${i}`; - // widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; - // this.widgets.push(widget) - // } - - for (let i = 0; i < 4; i++) { - const cat: any = { - name: `Category ${i + 1}`, - categoryId: i, - description: `Category ${i + 1} description`, - widgets: [] - } - - const randomNumber = Math.floor(Math.random() * (5 - 2 + 1)) + 2 - for (let j = 0; j < randomNumber; j++) { - const widget: any= {}; - widget.widgetId = `${i}-${j}` - widget.name = `Widget ${i}-${j}`; - widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; - cat.widgets.push(widget); - } - - this.widgetCategories.push(cat) - } - } - - initDashboard(dashboard: Dashboard) { - this.dashboardInstance = dashboard || new Dashboard() - } - - updateKey(key: any, value: any) { - this[key] = value - } - - resetCurrentWidget() { - this.currentWidget = new Widget() - } - - editWidget(widget: any) { - this.currentWidget.update(widget) - } - - fetchList() { - this.isLoading = true - - dashboardService.getDashboards() - .then((list: any) => { - runInAction(() => { - this.dashboards = list.map(d => new Dashboard().fromJson(d)) - }) - }).finally(() => { - runInAction(() => { - this.isLoading = false - }) - }) - } - - fetch(dashboardId: string) { - this.isLoading = true - dashboardService.getDashboard(dashboardId).then(response => { - runInAction(() => { - this.selectedDashboard = new Dashboard().fromJson(response) - }) - }).finally(() => { - runInAction(() => { - this.isLoading = false - }) - }) - } - - save(dashboard: IDashboard): Promise { - this.isSaving = true - const isCreating = !dashboard.dashboardId - return dashboardService.saveDashboard(dashboard).then(_dashboard => { - runInAction(() => { - if (isCreating) { - this.addDashboard(_dashboard) - } else { - this.updateDashboard(_dashboard) - } - }) - }).finally(() => { - runInAction(() => { - this.isSaving = false - }) - }) - } - - saveMetric(metric: IWidget, dashboardId: string): Promise { - const isCreating = !metric.widgetId - return dashboardService.saveMetric(metric, dashboardId).then(metric => { - runInAction(() => { - if (isCreating) { - this.selectedDashboard?.widgets.push(metric) - } else { - this.selectedDashboard?.widgets.map(w => { - if (w.widgetId === metric.widgetId) { - w.update(metric) - } - }) - } - }) - }) - } - - saveDashboardWidget(dashboard: Dashboard, widget: Widget) { - widget.validate() - if (widget.isValid) { - this.isLoading = true - if (widget.widgetId) { - this.client.put(`/dashboards/${dashboard.dashboardId}/widgets/${widget.widgetId}`, widget.toJson()) - .then(response => { - runInAction(() => { - this.isLoading = false - }) - } - ) - } else { - this.client.post(`/dashboards/${dashboard.dashboardId}/widgets`, widget.toJson()) - .then(response => { - runInAction(() => { - this.isLoading = false - }) - } - ) - } - } - } - - delete(dashboard: Dashboard) { - this.isLoading = true - this.client.delete(`/dashboards/${dashboard.dashboardId}`) - .then(response => { - runInAction(() => { - this.isLoading = false - }) - } - ) - } - - toJson() { - return { - dashboards: this.dashboards.map(d => d.toJson()) - } - } - - fromJson(json: any) { - runInAction(() => { - this.dashboards = json.dashboards.map(d => new Dashboard().fromJson(d)) - }) - return this - } - - addDashboard(dashboard: Dashboard) { - this.dashboards.push(dashboard) - } - - removeDashboard(dashboard: Dashboard) { - this.dashboards = this.dashboards.filter(d => d !== dashboard) - } - - getDashboard(dashboardId: string) { - return this.dashboards.find(d => d.dashboardId === dashboardId) - } - - getDashboardIndex(dashboardId: string) { - return this.dashboards.findIndex(d => d.dashboardId === dashboardId) - } - - getDashboardByIndex(index: number) { - return this.dashboards[index] - } - - getDashboardCount() { - return this.dashboards.length - } - - getDashboardIndexByDashboardId(dashboardId: string) { - return this.dashboards.findIndex(d => d.dashboardId === dashboardId) - } - - updateDashboard(dashboard: Dashboard) { - const index = this.dashboards.findIndex(d => d.dashboardId === dashboard.dashboardId) - if (index >= 0) { - this.dashboards[index] = dashboard - } - } - - selectDashboardById = (dashboardId: any) => { - this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || new Dashboard(); - if (this.selectedDashboard.dashboardId) { - this.fetch(this.selectedDashboard.dashboardId) - } - } - - setSiteId = (siteId: any) => { - this.siteId = siteId - } - - selectDefaultDashboard = (): Promise => { - return new Promise((resolve, reject) => { - if (this.dashboards.length > 0) { - const pinnedDashboard = this.dashboards.find(d => d.isPinned) - if (pinnedDashboard) { - this.selectedDashboard = pinnedDashboard - } else { - this.selectedDashboard = this.dashboards[0] - } - } - if (this.selectedDashboard) { - resolve(this.selectedDashboard) - } - - reject(new Error("No dashboards found")) - }) - } -} - -function getRandomWidget() { - const widget = new Widget(); - widget.widgetId = Math.floor(Math.random() * 100); - widget.name = randomMetricName(); - // widget.type = "random"; - widget.colSpan = Math.floor(Math.random() * 2) + 1; - return widget; -} - -function generateRandomPlaceName() { - const placeNames = [ - "New York", - "Los Angeles", - "Chicago", - "Houston", - "Philadelphia", - "Phoenix", - "San Antonio", - "San Diego", - ] - return placeNames[Math.floor(Math.random() * placeNames.length)] -} - - -function randomMetricName () { - const metrics = ["Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders"]; - return metrics[Math.floor(Math.random() * metrics.length)]; -} - -function getRandomDashboard(id: any = null, isPinned = false) { - const dashboard = new Dashboard(); - dashboard.name = generateRandomPlaceName(); - dashboard.dashboardId = id ? id : Math.floor(Math.random() * 10); - dashboard.isPinned = isPinned; - for (let i = 0; i < 8; i++) { - const widget = getRandomWidget(); - widget.position = i; - dashboard.addWidget(widget); - } - return dashboard; -} - -const sampleDashboards = [ - getRandomDashboard(1, true), - getRandomDashboard(2), - getRandomDashboard(3), - getRandomDashboard(4), -] \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/filter.ts b/frontend/app/components/Dashboard/store/filter.ts deleted file mode 100644 index c9bdd3452..000000000 --- a/frontend/app/components/Dashboard/store/filter.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" - -export default class Filter { - name: string = '' - filters: any[] = [] - eventsOrder: string = 'then' - - constructor() { - makeAutoObservable(this, { - addFilter: action, - removeFilter: action, - updateKey: action, - }) - } - - addFilter(filter: any) { - filter.value = [""] - if (filter.hasOwnProperty('filters')) { - filter.filters = filter.filters.map(i => ({ ...i, value: [""] })) - } - this.filters.push(filter) - console.log('addFilter', this.filters) - } - - updateFilter(index: number, filter: any) { - this.filters[index] = filter - console.log('updateFilter', this.filters) - } - - updateKey(key, value) { - this[key] = value - } - - removeFilter(index: number) { - this.filters.splice(index, 1) - } -} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/filterSeries.ts b/frontend/app/components/Dashboard/store/filterSeries.ts deleted file mode 100644 index 284e50230..000000000 --- a/frontend/app/components/Dashboard/store/filterSeries.ts +++ /dev/null @@ -1,22 +0,0 @@ -// import Filter from 'Types/filter'; -import Filter from './filter' -import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" - -export default class FilterSeries { - seriesId?: any = undefined - name: string = "Series 1" - filter: Filter = new Filter() - - constructor() { - makeAutoObservable(this, { - name: observable, - filter: observable, - - update: action, - }) - } - - update(key, value) { - this[key] = value - } -} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/index.ts b/frontend/app/components/Dashboard/store/index.ts deleted file mode 100644 index 6baa3c043..000000000 --- a/frontend/app/components/Dashboard/store/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DashboardStore } from './dashboardStore'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/store/widget.ts b/frontend/app/components/Dashboard/store/widget.ts deleted file mode 100644 index 5e624dd54..000000000 --- a/frontend/app/components/Dashboard/store/widget.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" -import Filter from 'Types/filter'; -import FilterSeries from "./filterSeries"; - -export interface IWidget { - widgetId: any - name: string - metricType: string - metricOf: string - metricValue: string - viewType: string - series: FilterSeries[] - sessions: [] - isPublic: boolean - owner: string - lastModified: Date - dashboardIds: any[] - - position: number - data: any - isLoading: boolean - isValid: boolean - dashboardId: any - colSpan: number - - udpateKey(key: string, value: any): void - removeSeries(index: number): void - addSeries(): void - fromJson(json: any): void - toJson(): any - validate(): void - update(data: any): void -} -export default class Widget implements IWidget { - public static get ID_KEY():string { return "widgetId" } - widgetId: any = undefined - name: string = "New Metric" - metricType: string = "timeseries" - metricOf: string = "sessionCount" - metricValue: string = "" - viewType: string = "lineChart" - series: FilterSeries[] = [] - sessions: [] = [] - isPublic: boolean = false - owner: string = "" - lastModified: Date = new Date() - dashboardIds: any[] = [] - - position: number = 0 - data: any = {} - isLoading: boolean = false - isValid: boolean = false - dashboardId: any = undefined - colSpan: number = 2 - - constructor() { - makeAutoObservable(this, { - widgetId: observable, - name: observable, - metricType: observable, - metricOf: observable, - position: observable, - data: observable, - isLoading: observable, - isValid: observable, - dashboardId: observable, - addSeries: action, - colSpan: observable, - - fromJson: action, - toJson: action, - validate: action, - update: action, - udpateKey: action, - }) - - const filterSeries = new FilterSeries() - this.series.push(filterSeries) - } - - udpateKey(key: string, value: any) { - this[key] = value - } - - removeSeries(index: number) { - this.series.splice(index, 1) - } - - addSeries() { - const series = new FilterSeries() - series.name = "Series " + (this.series.length + 1) - this.series.push(series) - } - - - fromJson(json: any) { - runInAction(() => { - this.widgetId = json.widgetId - this.name = json.name - this.data = json.data - }) - return this - } - - toJson() { - return { - widgetId: this.widgetId, - name: this.name, - metricOf: this.metricOf, - metricValue: this.metricValue, - viewType: this.viewType, - series: this.series, - sessions: this.sessions, - isPublic: this.isPublic, - } - } - - validate() { - this.isValid = this.name.length > 0 - } - - update(data: any) { - runInAction(() => { - Object.assign(this, data) - }) - } -} \ No newline at end of file diff --git a/frontend/app/components/shared/Filters/FilterList/FilterList.tsx b/frontend/app/components/shared/Filters/FilterList/FilterList.tsx index 110702ac7..5aab22bec 100644 --- a/frontend/app/components/shared/Filters/FilterList/FilterList.tsx +++ b/frontend/app/components/shared/Filters/FilterList/FilterList.tsx @@ -1,4 +1,4 @@ -import React, { useState} from 'react'; +import React, { useState, useEffect } from 'react'; import FilterItem from '../FilterItem'; import { SegmentSelection, Popup } from 'UI'; import { List } from 'immutable'; @@ -11,14 +11,17 @@ interface Props { onRemoveFilter: (filterIndex) => void; onChangeEventsOrder: (e, { name, value }) => void; hideEventsOrder?: boolean; + observeChanges?: () => void; } function FilterList(props: Props) { - const { filter, hideEventsOrder = false } = props; + const { observeChanges = () => {}, filter, hideEventsOrder = false } = props; const filters = List(filter.filters); const hasEvents = filters.filter((i: any) => i.isEvent).size > 0; const hasFilters = filters.filter((i: any) => !i.isEvent).size > 0; let rowIndex = 0; + useEffect(observeChanges, [filters]); + const onRemoveFilter = (filterIndex) => { props.onRemoveFilter(filterIndex); } diff --git a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx index ba7b8650e..2fd2fc132 100644 --- a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx +++ b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx @@ -12,7 +12,7 @@ interface Props { } function FilterValue(props: Props) { const { filter } = props; - const [durationValues, setDurationValues] = useState({ minDuration: filter.value[0], maxDuration: filter.value[1] }); + const [durationValues, setDurationValues] = useState({ minDuration: filter.value[0], maxDuration: filter.value.length > 1 ? filter.value[1] : filter.value[0] }); const showCloseButton = filter.value.length > 1; const lastIndex = filter.value.length - 1; diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index 9e73942f7..dcfd01058 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -17,10 +17,10 @@ document.addEventListener('DOMContentLoaded', () => { - - + {/* */} + {/* */} - + {/* */} diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 28e7197fb..96d7b22e0 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -1,13 +1,14 @@ import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" import Dashboard, { IDashboard } from "./types/dashboard" import Widget, { IWidget } from "./types/widget"; -import { dashboardService } from "App/services"; +import { dashboardService, metricService } from "App/services"; export interface IDashboardSotre { dashboards: IDashboard[] - widgetTemplates: any[] selectedDashboard: IDashboard | null dashboardInstance: IDashboard + selectedWidgets: IWidget[] + siteId: any currentWidget: Widget widgetCategories: any[] @@ -18,16 +19,22 @@ export interface IDashboardSotre { isLoading: boolean isSaving: boolean + isDeleting: boolean + fetchingDashboard: boolean + toggleAllSelectedWidgets: (isSelected: boolean) => void + removeSelectedWidgetByCategory(category: string): void + toggleWidgetSelection(widget: IWidget): void + initDashboard(dashboard?: IDashboard): void updateKey(key: string, value: any): void resetCurrentWidget(): void editWidget(widget: any): void fetchList(): Promise - fetch(dashboardId: string) + fetch(dashboardId: string): Promise save(dashboard: IDashboard): Promise saveDashboardWidget(dashboard: Dashboard, widget: Widget) - delete(dashboard: IDashboard) + deleteDashboard(dashboard: IDashboard): Promise toJson(): void fromJson(json: any): void initDashboard(dashboard: IDashboard): void @@ -43,14 +50,15 @@ export interface IDashboardSotre { selectDefaultDashboard(): Promise saveMetric(metric: IWidget, dashboardId?: string): Promise + fetchTemplates(): Promise } export default class DashboardStore implements IDashboardSotre { siteId: any = null // Dashbaord / Widgets dashboards: Dashboard[] = [] - widgetTemplates: any[] = [] - selectedDashboard: Dashboard | null = new Dashboard() + selectedDashboard: Dashboard | null = null dashboardInstance: IDashboard = new Dashboard() + selectedWidgets: IWidget[] = []; currentWidget: Widget = new Widget() widgetCategories: any[] = [] widgets: Widget[] = [] @@ -63,9 +71,13 @@ export default class DashboardStore implements IDashboardSotre { // Loading states isLoading: boolean = true isSaving: boolean = false + isDeleting: boolean = false + fetchingDashboard: boolean = false constructor() { makeAutoObservable(this, { + widgetCategories: observable.ref, + resetCurrentWidget: action, addDashboard: action, removeDashboard: action, @@ -82,41 +94,65 @@ export default class DashboardStore implements IDashboardSotre { setSiteId: action, editWidget: action, updateKey: action, + + toggleAllSelectedWidgets: action, + removeSelectedWidgetByCategory: action, + toggleWidgetSelection: action, + fetchTemplates: action, }) // TODO remove this sample data - // this.dashboards = sampleDashboards - // for (let i = 0; i < 15; i++) { - // const widget: any= {}; - // widget.widgetId = `${i}` - // widget.name = `Widget ${i}`; - // widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; - // this.widgets.push(widget) - // } - - for (let i = 0; i < 4; i++) { - const cat: any = { - name: `Category ${i + 1}`, - categoryId: i, - description: `Category ${i + 1} description`, - widgets: [] - } + // for (let i = 0; i < 4; i++) { + // const cat: any = { + // name: `Category ${i + 1}`, + // categoryId: i, + // description: `Category ${i + 1} description`, + // widgets: [] + // } - const randomNumber = Math.floor(Math.random() * (5 - 2 + 1)) + 2 - for (let j = 0; j < randomNumber; j++) { - const widget: any= {}; - widget.widgetId = `${i}-${j}` - widget.name = `Widget ${i}-${j}`; - widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; - cat.widgets.push(widget); - } + // const randomNumber = Math.floor(Math.random() * (5 - 2 + 1)) + 2 + // for (let j = 0; j < randomNumber; j++) { + // const widget: any= new Widget(); + // widget.widgetId = `${i}-${j}` + // widget.viewType = 'lineChart' + // widget.name = `Widget ${i}-${j}`; + // // widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; + // widget.metricType = 'timeseries'; + // cat.widgets.push(widget); + // } - this.widgetCategories.push(cat) + // this.widgetCategories.push(cat) + // } + } + + toggleAllSelectedWidgets(isSelected: boolean) { + if (isSelected) { + const allWidgets = this.widgetCategories.reduce((acc, cat) => { + return acc.concat(cat.widgets) + }, []) + + this.selectedWidgets = allWidgets + } else { + this.selectedWidgets = [] } } + removeSelectedWidgetByCategory = (category: any) => { + const categoryWidgetIds = category.widgets.map(w => w.metricId) + this.selectedWidgets = this.selectedWidgets.filter((widget: any) => !categoryWidgetIds.includes(widget.metricId)); + } + + toggleWidgetSelection = (widget: any) => { + const selectedWidgetIds = this.selectedWidgets.map((widget: any) => widget.metricId); + if (selectedWidgetIds.includes(widget.metricId)) { + this.selectedWidgets = this.selectedWidgets.filter((w: any) => w.metricId !== widget.metricId); + } else { + this.selectedWidgets.push(widget); + } + }; + findByIds(ids: string[]) { return this.dashboards.filter(d => ids.includes(d.dashboardId)) } @@ -152,22 +188,23 @@ export default class DashboardStore implements IDashboardSotre { }) } - fetch(dashboardId: string) { - this.isLoading = true - dashboardService.getDashboard(dashboardId).then(response => { - runInAction(() => { - this.selectedDashboard = new Dashboard().fromJson(response) - }) + fetch(dashboardId: string): Promise { + this.fetchingDashboard = true + return dashboardService.getDashboard(dashboardId).then(response => { + this.selectedDashboard = new Dashboard().fromJson(response) }).finally(() => { - runInAction(() => { - this.isLoading = false - }) + this.fetchingDashboard = false }) } save(dashboard: IDashboard): Promise { this.isSaving = true const isCreating = !dashboard.dashboardId + + if (isCreating) { + dashboard.metrics = this.selectedWidgets.map(w => w.metricId) + } + return dashboardService.saveDashboard(dashboard).then(_dashboard => { runInAction(() => { if (isCreating) { @@ -207,8 +244,17 @@ export default class DashboardStore implements IDashboardSotre { } } - delete(dashboard: Dashboard) { - this.isLoading = true + deleteDashboard(dashboard: Dashboard): Promise { + this.isDeleting = true + return dashboardService.deleteDashboard(dashboard.dashboardId).then(() => { + runInAction(() => { + this.removeDashboard(dashboard) + }) + }).finally(() => { + runInAction(() => { + this.isDeleting = false + }) + }) } toJson() { @@ -229,7 +275,7 @@ export default class DashboardStore implements IDashboardSotre { } removeDashboard(dashboard: Dashboard) { - this.dashboards = this.dashboards.filter(d => d !== dashboard) + this.dashboards = this.dashboards.filter(d => d.dashboardId !== dashboard.dashboardId) } getDashboard(dashboardId: string) { @@ -275,18 +321,43 @@ export default class DashboardStore implements IDashboardSotre { if (this.dashboards.length > 0) { const pinnedDashboard = this.dashboards.find(d => d.isPinned) if (pinnedDashboard) { - console.log('selecting pined dashboard') this.selectedDashboard = pinnedDashboard } else { - console.log('selecting first dashboard') this.selectedDashboard = this.dashboards[0] } } if (this.selectedDashboard) { resolve(this.selectedDashboard) } + // reject(new Error("No dashboards found")) + }) + } - reject(new Error("No dashboards found")) + fetchTemplates(): Promise { + return new Promise((resolve, reject) => { + if (this.widgetCategories.length > 0) { + resolve(this.widgetCategories) + } else { + metricService.getTemplates().then(response => { + const categories: any[] = [] + response.forEach(category => { + const widgets: any[] = [] + category.widgets.forEach(widget => { + const w = new Widget().fromJson(widget) + widgets.push(w) + }) + const c: any = {} + c.widgets = widgets + c.name = category.category + c.description = category.description + categories.push(c) + }) + this.widgetCategories = categories + resolve(this.widgetCategories) + }).catch(error => { + reject(error) + }) + } }) } } diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index e312ee58c..1e3746b9e 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -31,6 +31,7 @@ export interface IMetricStore { fetchList(): void fetch(metricId: string) delete(metric: IWidget) + // fetchMetricChartData(metric: IWidget) } export default class MetricStore implements IMetricStore { @@ -69,7 +70,6 @@ export default class MetricStore implements IMetricStore { fetch: action, delete: action, - paginatedList: computed, }) @@ -85,7 +85,7 @@ export default class MetricStore implements IMetricStore { // State Actions init(metric?: IWidget|null) { - this.instance = metric || new Widget() + this.instance.update(metric || new Widget()) } updateKey(key: string, value: any) { @@ -149,7 +149,7 @@ export default class MetricStore implements IMetricStore { this.isLoading = true return metricService.getMetrics() .then(metrics => { - this.metrics = metrics + this.metrics = metrics.map(m => new Widget().fromJson(m)) }).finally(() => { this.isLoading = false }) @@ -174,4 +174,17 @@ export default class MetricStore implements IMetricStore { this.isSaving = false }) } + + fetchMetricChartData(metric: IWidget) { + this.isLoading = true + return metricService.getMetricChartData(metric) + .then(data => { + console.log('data', data) + // runInAction(() => { + // metric.data = data + // }) + }).finally(() => { + this.isLoading = false + }) + } } \ No newline at end of file diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts index 7f3095744..46973f9ff 100644 --- a/frontend/app/mstore/types/dashboard.ts +++ b/frontend/app/mstore/types/dashboard.ts @@ -6,6 +6,7 @@ export interface IDashboard { name: string isPublic: boolean widgets: IWidget[] + metrics: any[] isValid: boolean isPinned: boolean currentWidget: IWidget @@ -24,13 +25,15 @@ export interface IDashboard { getWidgetIndexByWidgetId(widgetId: string): void swapWidgetPosition(positionA: number, positionB: number): void sortWidgets(): void + exists(): boolean } export default class Dashboard implements IDashboard { public static get ID_KEY():string { return "dashboardId" } dashboardId: any = undefined - name: string = "New Dashboard X" + name: string = "New Dashboard" isPublic: boolean = false widgets: IWidget[] = [] + metrics: any[] = [] isValid: boolean = false isPinned: boolean = false currentWidget: IWidget = new Widget() @@ -73,7 +76,9 @@ export default class Dashboard implements IDashboard { dashboardId: this.dashboardId, name: this.name, isPrivate: this.isPublic, - widgets: this.widgets.map(w => w.toJson()) + // widgets: this.widgets.map(w => w.toJson()) + // widgets: this.widgets + metrics: this.metrics } } @@ -149,4 +154,8 @@ export default class Dashboard implements IDashboard { } }) } + + exists() { + return this.dashboardId !== undefined + } } \ No newline at end of file diff --git a/frontend/app/mstore/types/filter.ts b/frontend/app/mstore/types/filter.ts index 5a7dcc5e9..bbe303e78 100644 --- a/frontend/app/mstore/types/filter.ts +++ b/frontend/app/mstore/types/filter.ts @@ -9,11 +9,14 @@ import FilterItem from "./filterItem" export default class Filter { public static get ID_KEY():string { return "filterId" } name: string = '' - filters: any[] = [] + filters: FilterItem[] = [] eventsOrder: string = 'then' constructor() { makeAutoObservable(this, { + filters: observable, + eventsOrder: observable, + addFilter: action, removeFilter: action, updateKey: action, diff --git a/frontend/app/mstore/types/filterItem.ts b/frontend/app/mstore/types/filterItem.ts index ea56020c5..606767b2b 100644 --- a/frontend/app/mstore/types/filterItem.ts +++ b/frontend/app/mstore/types/filterItem.ts @@ -19,6 +19,11 @@ export default class FilterItem { type: observable, key: observable, value: observable, + operator: observable, + source: observable, + filters: observable, + + merge: action }) this.merge(data) diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index c55ce2e97..4e6881b58 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -1,8 +1,9 @@ -import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import { makeAutoObservable, runInAction, observable, action, reaction, computed } from "mobx" import FilterSeries from "./filterSeries"; +import { DateTime } from 'luxon'; export interface IWidget { - metricId: string + metricId: any widgetId: any name: string metricType: string @@ -59,18 +60,20 @@ export default class Widget implements IWidget { constructor() { makeAutoObservable(this, { + // data: observable, widgetId: observable, name: observable, metricType: observable, metricOf: observable, position: observable, - data: observable, isLoading: observable, isValid: observable, dashboardId: observable, - addSeries: action, colSpan: observable, - + series: observable, + + addSeries: action, + removeSeries: action, fromJson: action, toJson: action, validate: action, @@ -97,14 +100,17 @@ export default class Widget implements IWidget { } fromJson(json: any) { - console.log('json', json); runInAction(() => { this.metricId = json.metricId this.widgetId = json.widgetId + this.metricValue = json.metricValue + this.metricOf = json.metricOf + this.metricType = json.metricType this.name = json.name - this.data = json.data - this.series = json.series.map((series: any) => new FilterSeries().fromJson(series)), + this.series = json.series ? json.series.map((series: any) => new FilterSeries().fromJson(series)) : [], this.dashboards = json.dashboards + this.owner = json.ownerEmail + this.lastModified = DateTime.fromISO(json.editedAt || json.createdAt) }) return this } diff --git a/frontend/app/services/DashboardService.ts b/frontend/app/services/DashboardService.ts index 1e5e8974f..a9d49a3bd 100644 --- a/frontend/app/services/DashboardService.ts +++ b/frontend/app/services/DashboardService.ts @@ -1,6 +1,6 @@ -import { IDashboard } from "App/components/Dashboard/store/dashboard"; +import { IDashboard } from "App/mstore/types/dashboard"; import APIClient from 'App/api_client'; -import { IWidget } from "App/components/Dashboard/store/widget"; +import { IWidget } from "App/mstore/types/widget"; export interface IDashboardService { initClient(client?: APIClient) diff --git a/frontend/app/services/MetricService.ts b/frontend/app/services/MetricService.ts index 99c935d47..f2fab0a83 100644 --- a/frontend/app/services/MetricService.ts +++ b/frontend/app/services/MetricService.ts @@ -10,6 +10,7 @@ export interface IMetricService { deleteMetric(metricId: string): Promise; getTemplates(): Promise; + getMetricChartData(metric: IWidget): Promise; } export default class MetricService implements IMetricService { @@ -88,4 +89,11 @@ export default class MetricService implements IMetricService { .then(response => response.json()) .then(response => response.data || []); } + + getMetricChartData(metric: IWidget): Promise { + const path = metric.metricId ? `/metrics/${metric.metricId}/chart` : `/custom_metrics/try`; + return this.client.get(path) + .then(response => response.json()) + .then(response => response.data || {}); + } } \ No newline at end of file From 2cdf6524bf715575338d2d6b383bf3fdf07f8146 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 5 Apr 2022 17:00:38 +0200 Subject: [PATCH 44/87] feat(ui) - dashboard - wip --- frontend/tailwind.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 28fd4d982..04c57948e 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -86,7 +86,7 @@ module.exports = { 'textAlign', // 'textColor', // 'textOpacity', - // 'textDecoration', + 'textDecoration', 'textTransform', // 'transform', // 'transitionDuration', @@ -109,6 +109,7 @@ module.exports = { }, extend: {}, }, + content: [], variants: { visibility: ['responsive', 'hover', 'focus', 'group-hover'] }, From effe39d36d7a24bcdeafff1766676efed0abea7d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 17:29:29 +0200 Subject: [PATCH 45/87] feat(api): pg helper support application name feat(api): removed widget name feat(db): removed widget name feat(api): changed edit dashboard to support list of metrics feat(api): get metrics/widgets cast created_at feat(api): get metrics/widgets cast edited_at feat(api): create dashboard with metrics, support default config feat(api): create dashboard with metrics, support widget position feat(api): edit dashboard with metrics, support default config feat(api): edit dashboard with metrics, support widget position --- api/Dockerfile | 1 + api/Dockerfile.alerts | 1 + api/chalicelib/core/custom_metrics.py | 7 +++- api/chalicelib/core/dashboards2.py | 39 +++++++++++++----- api/chalicelib/utils/pg_client.py | 5 ++- api/schemas.py | 14 +++---- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 41 +++++++++---------- .../db/init_dbs/postgresql/init_schema.sql | 38 ++++++++--------- 8 files changed, 84 insertions(+), 62 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index 780518ff3..4526c32bd 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /work COPY . . RUN pip install -r requirements.txt RUN mv .env.default .env +ENV APP_NAME chalice # Add Tini # Startup daemon diff --git a/api/Dockerfile.alerts b/api/Dockerfile.alerts index ed8f06eac..bdd1772ba 100644 --- a/api/Dockerfile.alerts +++ b/api/Dockerfile.alerts @@ -6,6 +6,7 @@ COPY . . RUN pip install -r requirements.txt RUN mv .env.default .env && mv app_alerts.py app.py ENV pg_minconn 2 +ENV APP_NAME alerts # Add Tini # Startup daemon diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 51f8655a4..6ca72f453 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -233,7 +233,7 @@ def get_all(project_id, user_id, include_series=False): WHERE metrics.project_id = %(project_id)s AND metrics.deleted_at ISNULL AND (user_id = %(user_id)s OR metrics.is_public) - ORDER BY created_at;""", + ORDER BY metrics.edited_at, metrics.created_at;""", {"project_id": project_id, "user_id": user_id} ) ) @@ -243,6 +243,10 @@ def get_all(project_id, user_id, include_series=False): # r["created_at"] = TimeUTC.datetime_to_timestamp(r["created_at"]) for s in r["series"]: s["filter"] = helper.old_search_payload_to_flat(s["filter"]) + else: + for r in rows: + r["created_at"] = TimeUTC.datetime_to_timestamp(r["created_at"]) + r["edited_at"] = TimeUTC.datetime_to_timestamp(r["edited_at"]) rows = helper.list_to_camel_case(rows) return rows @@ -297,6 +301,7 @@ def get(metric_id, project_id, user_id, flatten=True): if row is None: return None row["created_at"] = TimeUTC.datetime_to_timestamp(row["created_at"]) + row["edited_at"] = TimeUTC.datetime_to_timestamp(row["edited_at"]) if flatten: for s in row["series"]: s["filter"] = helper.old_search_payload_to_flat(s["filter"]) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 7e06f52ac..c0bb3017f 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -32,11 +32,15 @@ def create_dashboard(project_id, user_id, data: schemas.CreateDashboardSchema): params = {"userId": user_id, "projectId": project_id, **data.dict()} if data.metrics is not None and len(data.metrics) > 0: pg_query = f"""WITH dash AS ({pg_query}) - INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id) - VALUES {",".join([f"((SELECT dashboard_id FROM dash),%(metric_id_{i})s, %(userId)s)" for i in range(len(data.metrics))])} + INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config) + VALUES {",".join([f"((SELECT dashboard_id FROM dash),%(metric_id_{i})s, %(userId)s, %(config_{i})s)" for i in range(len(data.metrics))])} RETURNING (SELECT dashboard_id FROM dash)""" - for i, m in enumerate(data.metrics): - params[f"metric_id_{i}"] = m + for i, m in enumerate(data.metrics): + params[f"metric_id_{i}"] = m + params[f"config_{i}"] = schemas.AddWidgetToDashboardPayloadSchema.schema() \ + .get("properties", {}).get("config", {}).get("default", {}) + params[f"config_{i}"]["position"] = i + params[f"config_{i}"] = json.dumps(params[f"config_{i}"]) cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() if row is None: @@ -98,16 +102,29 @@ def delete_dashboard(project_id, user_id, dashboard_id): def update_dashboard(project_id, user_id, dashboard_id, data: schemas.EditDashboardSchema): with pg_client.PostgresClient() as cur: - pg_query = """UPDATE dashboards - SET name = %(name)s, is_public = %(is_public)s + pg_query = f"""UPDATE dashboards + SET name = %(name)s + {", is_public = %(is_public)s" if data.is_public is not None else ""} + {", is_pinned = %(is_pinned)s" if data.is_pinned is not None else ""} WHERE dashboards.project_id = %(projectId)s AND dashboard_id = %(dashboard_id)s - AND (dashboards.user_id = %(userId)s OR is_public) - RETURNING *;""" + AND (dashboards.user_id = %(userId)s OR is_public)""" params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()} + if data.metrics is not None and len(data.metrics) > 0: + pg_query = f"""WITH dash AS ({pg_query}) + INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config) + VALUES {",".join([f"(%(dashboard_id)s, %(metric_id_{i})s, %(userId)s, %(config_{i})s)" for i in range(len(data.metrics))])} + RETURNING (SELECT dashboard_id FROM dash)""" + for i, m in enumerate(data.metrics): + params[f"metric_id_{i}"] = m + params[f"config_{i}"] = schemas.AddWidgetToDashboardPayloadSchema.schema() \ + .get("properties", {}).get("config", {}).get("default", {}) + params[f"config_{i}"]["position"] = i + params[f"config_{i}"] = json.dumps(params[f"config_{i}"]) + cur.execute(cur.mogrify(pg_query, params)) - row = cur.fetchone() - return helper.dict_to_camel_case(row) + + return get_dashboard(project_id=project_id, user_id=user_id, dashboard_id=dashboard_id) def get_widget(project_id, user_id, dashboard_id, widget_id): @@ -154,7 +171,7 @@ def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashb def update_widget(project_id, user_id, dashboard_id, widget_id, data: schemas.AddWidgetToDashboardPayloadSchema): with pg_client.PostgresClient() as cur: pg_query = """UPDATE dashboard_widgets - SET name= %(name)s, config= %(config)s + SET config= %(config)s WHERE dashboard_id=%(dashboard_id)s AND widget_id=%(widget_id)s RETURNINIG *;""" params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, diff --git a/api/chalicelib/utils/pg_client.py b/api/chalicelib/utils/pg_client.py index 6e4118689..6379bad1e 100644 --- a/api/chalicelib/utils/pg_client.py +++ b/api/chalicelib/utils/pg_client.py @@ -9,7 +9,8 @@ _PG_CONFIG = {"host": config("pg_host"), "database": config("pg_dbname"), "user": config("pg_user"), "password": config("pg_password"), - "port": config("pg_port", cast=int)} + "port": config("pg_port", cast=int), + "application_name": config("APP_NAME", default="PY")} PG_CONFIG = dict(_PG_CONFIG) if config("pg_timeout", cast=int, default=0) > 0: PG_CONFIG["options"] = f"-c statement_timeout={config('pg_timeout', cast=int) * 1000}" @@ -64,6 +65,8 @@ class PostgresClient: def __init__(self, long_query=False): self.long_query = long_query if long_query: + long_config = dict(_PG_CONFIG) + long_config["application_name"] += "-LONG" self.connection = psycopg2.connect(**_PG_CONFIG) else: self.connection = postgreSQL_pool.getconn() diff --git a/api/schemas.py b/api/schemas.py index a7fd003b7..1f1bec739 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -888,18 +888,15 @@ class CreateDashboardSchema(BaseModel): alias_generator = attribute_to_camel_case -class EditDashboardSchema(BaseModel): - name: str = Field(..., min_length=1) - is_public: bool = Field(default=False) - - class Config: - alias_generator = attribute_to_camel_case +class EditDashboardSchema(CreateDashboardSchema): + is_public: Optional[bool] = Field(default=None) + is_pinned: Optional[bool] = Field(default=None) class AddWidgetToDashboardPayloadSchema(BaseModel): metric_id: int = Field(default=None) - name: Optional[str] = Field(default=None) - config: dict = Field(default={}) + # if you change the config attribute name, please make sure to update it in dashboard2.py + config: dict = Field(default={"col": 1, "row": 1, "position": 0}) class Config: alias_generator = attribute_to_camel_case @@ -929,7 +926,6 @@ class TemplateKeys(str, Enum): avg_fps = "avg_fps" -# class CustomMetricAndTemplate(CreateCustomMetricsSchema): class CustomMetricAndTemplate(BaseModel): is_template: bool = Field(...) project_id: Optional[int] = Field(...) diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 6c563bfdc..1a3a918ed 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -45,31 +45,30 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), - config jsonb NOT NULL DEFAULT '{}'::jsonb, - name text + config jsonb NOT NULL DEFAULT '{}'::jsonb ); INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) -VALUES ('sessions count', 'overview', '{}', true, true, true, 'count_sessions'), - ('avg request load time', 'overview', '{}', true, true, true, 'avg_request_load_time'), - ('avg page load time', 'overview', '{}', true, true, true, 'avg_page_load_time'), - ('avg image load time', 'overview', '{}', true, true, true, 'avg_image_load_time'), - ('avg dom content load start', 'overview', '{}', true, true, true, 'avg_dom_content_load_start'), - ('avg first contentful pixel', 'overview', '{}', true, true, true, 'avg_first_contentful_pixel'), - ('avg visited pages count', 'overview', '{}', true, true, true, 'avg_visited_pages'), - ('avg session duration', 'overview', '{}', true, true, true, 'avg_session_duration'), - ('avg pages dom build time', 'overview', '{}', true, true, true, 'avg_pages_dom_buildtime'), - ('avg pages response time', 'overview', '{}', true, true, true, 'avg_pages_response_time'), - ('avg response time', 'overview', '{}', true, true, true, 'avg_response_time'), - ('avg first paint', 'overview', '{}', true, true, true, 'avg_first_paint'), - ('avg dom content loaded', 'overview', '{}', true, true, true, 'avg_dom_content_loaded'), - ('avg time till first bit', 'overview', '{}', true, true, true, 'avg_till_first_bit'), - ('avg time to interactive', 'overview', '{}', true, true, true, 'avg_time_to_interactive'), - ('requests count', 'overview', '{}', true, true, true, 'count_requests'), - ('avg time to render', 'overview', '{}', true, true, true, 'avg_time_to_render'), - ('avg used js heap size', 'overview', '{}', true, true, true, 'avg_used_js_heap_size'), - ('avg cpu', 'overview', '{}', true, true, true, 'avg_cpu') +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu') ON CONFLICT (key) DO UPDATE SET name=excluded.name, category=excluded.category, config=excluded.config, diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 5b5bd7044..5114d2433 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1026,25 +1026,25 @@ $$ LANGUAGE plpgsql; INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) -VALUES ('sessions count', 'overview', '{}', true, true, true, 'count_sessions'), - ('avg request load time', 'overview', '{}', true, true, true, 'avg_request_load_time'), - ('avg page load time', 'overview', '{}', true, true, true, 'avg_page_load_time'), - ('avg image load time', 'overview', '{}', true, true, true, 'avg_image_load_time'), - ('avg dom content load start', 'overview', '{}', true, true, true, 'avg_dom_content_load_start'), - ('avg first contentful pixel', 'overview', '{}', true, true, true, 'avg_first_contentful_pixel'), - ('avg visited pages count', 'overview', '{}', true, true, true, 'avg_visited_pages'), - ('avg session duration', 'overview', '{}', true, true, true, 'avg_session_duration'), - ('avg pages dom build time', 'overview', '{}', true, true, true, 'avg_pages_dom_buildtime'), - ('avg pages response time', 'overview', '{}', true, true, true, 'avg_pages_response_time'), - ('avg response time', 'overview', '{}', true, true, true, 'avg_response_time'), - ('avg first paint', 'overview', '{}', true, true, true, 'avg_first_paint'), - ('avg dom content loaded', 'overview', '{}', true, true, true, 'avg_dom_content_loaded'), - ('avg time till first bit', 'overview', '{}', true, true, true, 'avg_till_first_bit'), - ('avg time to interactive', 'overview', '{}', true, true, true, 'avg_time_to_interactive'), - ('requests count', 'overview', '{}', true, true, true, 'count_requests'), - ('avg time to render', 'overview', '{}', true, true, true, 'avg_time_to_render'), - ('avg used js heap size', 'overview', '{}', true, true, true, 'avg_used_js_heap_size'), - ('avg cpu', 'overview', '{}', true, true, true, 'avg_cpu') +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu') ON CONFLICT (key) DO UPDATE SET name=excluded.name, category=excluded.category, config=excluded.config, From 3dd8b040395ac57ae2c9f29a7710ade2c9e05e0d Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 5 Apr 2022 17:48:06 +0200 Subject: [PATCH 46/87] feat(ui) - dashboard - wip --- .../DashboardModal/DashboardModal.tsx | 16 +++++++++++--- .../DashboardView/DashboardView.tsx | 4 ++-- .../DashboardWidgetGrid.tsx | 10 +++++++-- .../WidgetWrapper/WidgetWrapper.tsx | 22 ++++++++++++++++++- frontend/app/mstore/dashboardStore.ts | 17 +++++++++++--- frontend/app/services/DashboardService.ts | 10 --------- 6 files changed, 58 insertions(+), 21 deletions(-) diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 8c102763b..07be9fb84 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -3,11 +3,19 @@ import { useObserver } from 'mobx-react-lite'; import DashboardMetricSelection from '../DashboardMetricSelection'; import DashboardForm from '../DashboardForm'; import { Button } from 'UI'; +import { withRouter } from 'react-router-dom'; import { useStore } from 'App/mstore'; import { useModal } from 'App/components/Modal'; +import { dashboardMetricCreate, withSiteId } from 'App/routes'; - +interface Props { + history: any + siteId?: string + dashboardId?: string +} function DashboardModal(props) { + const { history, siteId, dashboardId } = props; + console.log('DashboardModal', props); const { dashboardStore } = useStore(); const { hideModal } = useModal(); const dashboard = useObserver(() => dashboardStore.dashboardInstance); @@ -18,6 +26,8 @@ function DashboardModal(props) { } const handleCreateNew = () => { + const path = withSiteId(dashboardMetricCreate(dashboardId), siteId); + history.push(path); hideModal(); } @@ -33,7 +43,7 @@ function DashboardModal(props) {
- + {dashboard.exists() && }
{ !dashboard.exists() && ( @@ -58,4 +68,4 @@ function DashboardModal(props) { )); } -export default DashboardModal; \ No newline at end of file +export default withRouter(DashboardModal); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index 3c0f96a92..2b9f9db98 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -29,7 +29,7 @@ function DashboardView(props: Props) { const onEditHandler = () => { dashboardStore.initDashboard(dashboard) - showModal(, {}) + showModal(, {}) } const onDelete = async () => { @@ -64,7 +64,7 @@ function DashboardView(props: Props) {
- +
diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx index b52a25329..9bb7a1ce5 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx @@ -4,11 +4,16 @@ import WidgetWrapper from '../WidgetWrapper'; import { NoContent, Button, Loader } from 'UI'; import { useObserver } from 'mobx-react-lite'; +interface Props { + dashboardId: string; + onEditHandler: () => void; +} function DashboardWidgetGrid(props) { + const { dashboardId } = props; const { dashboardStore } = useStore(); const loading = useObserver(() => dashboardStore.isLoading); const dashbaord: any = dashboardStore.selectedDashboard; - const list: any = dashbaord?.widgets; + const list: any = useObserver(() => dashbaord?.widgets); return useObserver(() => ( @@ -19,7 +24,7 @@ function DashboardWidgetGrid(props) { subtext={

Metrics helps you visualize trends from sessions captured by OpenReplay

- +
} > @@ -30,6 +35,7 @@ function DashboardWidgetGrid(props) { widget={item} key={item.widgetId} moveListItem={(dragIndex, hoverIndex) => dashbaord.swapWidgetPosition(dragIndex, hoverIndex)} + dashboardId={dashboardId} /> ))}
diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 6a43577a2..f426dac6e 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -4,6 +4,8 @@ import { ItemMenu } from 'UI'; import { useDrag, useDrop } from 'react-dnd'; import WidgetChart from '../WidgetChart'; import { useObserver } from 'mobx-react-lite'; +import { confirm } from 'UI/Confirmation'; +import { useStore } from 'App/mstore'; interface Props { className?: string; @@ -11,9 +13,11 @@ interface Props { index?: number; moveListItem?: any; isPreview?: boolean; + dashboardId?: string; } function WidgetWrapper(props: Props) { - const { widget, index = 0, moveListItem = null, isPreview = false } = props; + const { dashboardStore } = useStore(); + const { widget, index = 0, moveListItem = null, isPreview = false, dashboardId } = props; const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -37,6 +41,18 @@ function WidgetWrapper(props: Props) { }), }) + const onDelete = async () => { + if (await confirm({ + header: 'Confirm', + confirmButton: 'Yes, Delete', + confirmation: `Are you sure you want to permanently delete this Dashboard?` + })) { + dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId).then(() => { + + }) + } + } + const ref: any = useRef(null) const dragDropRef: any = dragRef(dropRef(ref)) @@ -64,6 +80,10 @@ function WidgetWrapper(props: Props) { console.log('edit'); } }, + { + text: 'Hide from view' + dashboardId, + onClick: onDelete + }, ]} />
diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 96d7b22e0..1b601cab4 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -51,6 +51,7 @@ export interface IDashboardSotre { saveMetric(metric: IWidget, dashboardId?: string): Promise fetchTemplates(): Promise + deleteDashboardWidget(dashboardId: string, widgetId: string): Promise } export default class DashboardStore implements IDashboardSotre { siteId: any = null @@ -159,6 +160,7 @@ export default class DashboardStore implements IDashboardSotre { initDashboard(dashboard: Dashboard) { this.dashboardInstance = dashboard || new Dashboard() + this.selectedWidgets = [] } updateKey(key: any, value: any) { @@ -201,9 +203,7 @@ export default class DashboardStore implements IDashboardSotre { this.isSaving = true const isCreating = !dashboard.dashboardId - if (isCreating) { - dashboard.metrics = this.selectedWidgets.map(w => w.metricId) - } + dashboard.metrics = this.selectedWidgets.map(w => w.metricId) return dashboardService.saveDashboard(dashboard).then(_dashboard => { runInAction(() => { @@ -360,6 +360,17 @@ export default class DashboardStore implements IDashboardSotre { } }) } + + deleteDashboardWidget(dashboardId: string, widgetId: string) { + this.isDeleting = true + return dashboardService.deleteWidget(dashboardId, widgetId).then(() => { + runInAction(() => { + this.selectedDashboard?.removeWidget(widgetId) + }) + }).finally(() => { + this.isDeleting = false + }) + } } function getRandomWidget() { diff --git a/frontend/app/services/DashboardService.ts b/frontend/app/services/DashboardService.ts index a9d49a3bd..3208fd941 100644 --- a/frontend/app/services/DashboardService.ts +++ b/frontend/app/services/DashboardService.ts @@ -13,7 +13,6 @@ export interface IDashboardService { deleteDashboard(dashboardId: string): Promise saveMetric(metric: IWidget, dashboardId?: string): Promise - deleteMetric(metricId: string): Promise saveWidget(dashboardId: string, widget: IWidget): Promise deleteWidget(dashboardId: string, widgetId: string): Promise @@ -111,15 +110,6 @@ export default class DashboardService implements IDashboardService { } } - /** - * Delete a Metric by metricId. - * @param metricId - * @returns {Promise} - */ - deleteMetric(metricId: string): Promise { - return this.client.delete(`/metrics/${metricId}`) - } - /** * Remove a widget from a dashboard. * @param dashboardId Required From 6685eb1ac9d53d0c56815f9bd4d4e74b6664d203 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 18:10:19 +0200 Subject: [PATCH 47/87] feat(api): fixed create custom metric and add to dashboard feat(api): EE refactored sub-router feat(api): EE upgrade fastapi feat(api): EE refactored metrics feat(api): EE split overview --- api/chalicelib/core/dashboards2.py | 4 +- ee/api/.gitignore | 9 +- ee/api/Dockerfile | 1 + ee/api/Dockerfile.alerts | 1 + ee/api/_clickhouse_upgrade.sh | 10 - ee/api/app.py | 4 +- ee/api/chalicelib/core/dashboard.py | 708 +++++++++++++++++----- ee/api/requirements.txt | 2 +- ee/api/routers/{app => subs}/v1_api_ee.py | 0 9 files changed, 579 insertions(+), 160 deletions(-) delete mode 100644 ee/api/_clickhouse_upgrade.sh rename ee/api/routers/{app => subs}/v1_api_ee.py (100%) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index c0bb3017f..3efd1e968 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -153,9 +153,9 @@ def get_widget(project_id, user_id, dashboard_id, widget_id): def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashboardPayloadSchema): with pg_client.PostgresClient() as cur: - pg_query = """INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config, name) + pg_query = """INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config) SELECT %(dashboard_id)s AS dashboard_id, %(metric_id)s AS metric_id, - %(userId)s AS user_id, %(config)s::jsonb AS config, %(name)s AS name + %(userId)s AS user_id, %(config)s::jsonb AS config WHERE EXISTS(SELECT 1 FROM dashboards WHERE dashboards.deleted_at ISNULL AND dashboards.project_id = %(projectId)s AND dashboard_id = %(dashboard_id)s diff --git a/ee/api/.gitignore b/ee/api/.gitignore index f1ff9550b..af91e1919 100644 --- a/ee/api/.gitignore +++ b/ee/api/.gitignore @@ -180,9 +180,6 @@ Pipfile /chalicelib/core/alerts.py /chalicelib/core/alerts_processor.py /chalicelib/core/announcements.py -/chalicelib/blueprints/bp_app_api.py -/chalicelib/blueprints/bp_core.py -/chalicelib/blueprints/bp_core_crons.py /chalicelib/core/collaboration_slack.py /chalicelib/core/errors_favorite_viewed.py /chalicelib/core/events.py @@ -237,7 +234,6 @@ Pipfile /chalicelib/utils/smtp.py /chalicelib/utils/strings.py /chalicelib/utils/TimeUTC.py -/chalicelib/blueprints/app/__init__.py /routers/app/__init__.py /routers/crons/__init__.py /routers/subs/__init__.py @@ -245,7 +241,6 @@ Pipfile /chalicelib/core/assist.py /auth/auth_apikey.py /auth/auth_jwt.py -/chalicelib/blueprints/subs/bp_insights.py /build.sh /routers/core.py /routers/crons/core_crons.py @@ -257,10 +252,10 @@ Pipfile /chalicelib/core/heatmaps.py /routers/subs/insights.py /schemas.py -/chalicelib/blueprints/app/v1_api.py -/routers/app/v1_api.py /chalicelib/core/custom_metrics.py /chalicelib/core/performance_event.py /chalicelib/core/saved_search.py /app_alerts.py /build_alerts.sh +/routers/subs/metrics.py +/routers/subs/v1_api.py diff --git a/ee/api/Dockerfile b/ee/api/Dockerfile index cca6e6806..ee88ee22c 100644 --- a/ee/api/Dockerfile +++ b/ee/api/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /work COPY . . RUN pip install -r requirements.txt RUN mv .env.default .env +ENV APP_NAME chalice # Add Tini # Startup daemon diff --git a/ee/api/Dockerfile.alerts b/ee/api/Dockerfile.alerts index 9be6ebc93..230514918 100644 --- a/ee/api/Dockerfile.alerts +++ b/ee/api/Dockerfile.alerts @@ -7,6 +7,7 @@ COPY . . RUN pip install -r requirements.txt RUN mv .env.default .env && mv app_alerts.py app.py ENV pg_minconn 2 +ENV APP_NAME alerts # Add Tini # Startup daemon diff --git a/ee/api/_clickhouse_upgrade.sh b/ee/api/_clickhouse_upgrade.sh deleted file mode 100644 index 9b656a584..000000000 --- a/ee/api/_clickhouse_upgrade.sh +++ /dev/null @@ -1,10 +0,0 @@ -sudo yum update -sudo yum install yum-utils -sudo rpm --import https://repo.clickhouse.com/CLICKHOUSE-KEY.GPG -sudo yum-config-manager --add-repo https://repo.clickhouse.com/rpm/stable/x86_64 -sudo yum update -sudo service clickhouse-server restart - - -#later mus use in clickhouse-client: -#SET allow_experimental_window_functions = 1; \ No newline at end of file diff --git a/ee/api/app.py b/ee/api/app.py index deae9d78d..ed2c01aa4 100644 --- a/ee/api/app.py +++ b/ee/api/app.py @@ -11,10 +11,10 @@ from starlette.responses import StreamingResponse, JSONResponse from chalicelib.utils import helper from chalicelib.utils import pg_client from routers import core, core_dynamic, ee, saml -from routers.app import v1_api, v1_api_ee +from routers.subs import v1_api from routers.crons import core_crons from routers.crons import core_dynamic_crons -from routers.subs import dashboard, insights +from routers.subs import dashboard, insights, v1_api_ee app = FastAPI() diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index c5c373c78..c834c852b 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -169,7 +169,7 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) ch_query = f"""\ SELECT toUnixTimestamp(toStartOfInterval(sessions.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COUNT(sessions.session_id) AS count + COUNT(sessions.session_id) AS value FROM sessions {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -181,19 +181,17 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) rows = ch.execute(query=ch_query, params=params) results = { - "count": sum([r["count"] for r in rows]), + "value": sum([r["value"] for r in rows]), "chart": __complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, - neutral={"count": 0}) + neutral={"value": 0}) } diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff - ch_query = f"""\ - SELECT - COUNT(sessions.session_id) AS count + ch_query = f""" SELECT COUNT(sessions.session_id) AS count FROM sessions {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)};""" params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -203,7 +201,7 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) count = count[0]["count"] - results["countProgress"] = helper.__progress(old_val=count, new_val=results["count"]) + results["progress"] = helper.__progress(old_val=count, new_val=results["value"]) return results @@ -222,9 +220,8 @@ def get_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimesta with ch_client.ClickHouseClient() as ch: ch_query = f"""\ - SELECT - toUnixTimestamp(toStartOfInterval(errors.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COUNT(DISTINCT errors.session_id) AS count + SELECT toUnixTimestamp(toStartOfInterval(errors.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, + COUNT(DISTINCT errors.session_id) AS count FROM errors {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -304,9 +301,8 @@ def get_errors_trend(project_id, startTimestamp=TimeUTC.now(delta_days=-1), errors = {} for error_id in error_ids: ch_query = f"""\ - SELECT - toUnixTimestamp(toStartOfInterval(errors.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COUNT(errors.session_id) AS count + SELECT toUnixTimestamp(toStartOfInterval(errors.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, + COUNT(errors.session_id) AS count FROM errors {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -348,10 +344,8 @@ def __get_page_metrics(ch, project_id, startTimestamp, endTimestamp, **args): ch_sub_query += meta_condition # changed dom_content_loaded_event_start to dom_content_loaded_event_end ch_query = f"""\ - SELECT - COALESCE(AVG(NULLIF(pages.dom_content_loaded_event_end ,0)),0) AS avg_dom_content_load_start, --- COALESCE(AVG(NULLIF(pages.dom_content_loaded_event_start ,0)),0) AS avg_dom_content_load_start, - COALESCE(AVG(NULLIF(pages.first_contentful_paint,0)),0) AS avg_first_contentful_pixel + SELECT COALESCE(AVG(NULLIF(pages.dom_content_loaded_event_end ,0)),0) AS avg_dom_content_load_start, + COALESCE(AVG(NULLIF(pages.first_contentful_paint,0)),0) AS avg_first_contentful_pixel FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)};""" params = {"project_id": project_id, "type": 'fetch', "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -395,8 +389,7 @@ def __get_application_activity(ch, project_id, startTimestamp, endTimestamp, **a ch_sub_query += meta_condition ch_sub_query.append("resources.type= %(type)s") ch_query = f"""\ - SELECT - AVG(NULLIF(resources.duration,0)) AS avg + SELECT AVG(NULLIF(resources.duration,0)) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)};""" row = ch.execute(query=ch_query, @@ -443,9 +436,8 @@ def __get_user_activity(cur, project_id, startTimestamp, endTimestamp, **args): ch_sub_query += meta_condition ch_query = f"""\ - SELECT - COALESCE(CEIL(AVG(NULLIF(sessions.pages_count,0))),0) AS avg_visited_pages, - COALESCE(AVG(NULLIF(sessions.duration,0)),0) AS avg_session_duration + SELECT COALESCE(CEIL(AVG(NULLIF(sessions.pages_count,0))),0) AS avg_visited_pages, + COALESCE(AVG(NULLIF(sessions.duration,0)),0) AS avg_session_duration FROM sessions {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)};""" params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -471,8 +463,8 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT resources.url, - AVG(NULLIF(resources.duration,0)) AS avg, - COUNT(resources.session_id) AS count + AVG(NULLIF(resources.duration,0)) AS avg, + COUNT(resources.session_id) AS count FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY resources.url ORDER BY avg DESC LIMIT 10;""" @@ -489,7 +481,7 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), for url in urls: ch_query = f"""\ SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(resources.duration,0)) AS avg + AVG(NULLIF(resources.duration,0)) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -544,9 +536,8 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp} with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(resources.duration,0)) AS avg + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(resources.duration,0)) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} AND resources.type = 'img' @@ -558,9 +549,8 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi __complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, neutral={"avg": 0})] - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(resources.duration,0)) AS avg + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(resources.duration,0)) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} AND resources.type = 'fetch' @@ -577,9 +567,8 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi data=args) ch_sub_query_chart += meta_condition - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(pages.load_event_end ,0)) AS avg + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(pages.load_event_end ,0)) AS avg FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} {(f' AND ({" OR ".join(location_constraints)})') if len(location_constraints) > 0 else ""} @@ -648,9 +637,8 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, if resource_type == "ALL" and not pages_only and not events_only: ch_sub_query.append("positionUTF8(url_hostpath,%(value)s)!=0") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - arrayJoin(arraySlice(arrayReverseSort(arrayDistinct(groupArray(url))), 1, 5)) AS value, - type AS key + ch_query = f"""SELECT arrayJoin(arraySlice(arrayReverseSort(arrayDistinct(groupArray(url))), 1, 5)) AS value, + type AS key FROM resources WHERE {" AND ".join(ch_sub_query)} GROUP BY type @@ -685,9 +673,8 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, ch_sub_query.append(f"resources.type = '{__get_resource_db_type_from_type(resource_type)}'") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - DISTINCT url_hostpath AS value, - %(resource_type)s AS key + ch_query = f"""SELECT DISTINCT url_hostpath AS value, + %(resource_type)s AS key FROM resources WHERE {" AND ".join(ch_sub_query)} LIMIT 10;""" @@ -787,34 +774,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, return [helper.dict_to_camel_case(row) for row in rows] -# def frustration_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1), -# endTimestamp=TimeUTC.now(), **args): -# with pg_client.PostgresClient() as cur: -# sub_q = "" -# if platform == 'mobile': -# sub_q = "AND s.user_device_type = 'mobile' AND s.project_id = %(project_id)s AND s.start_ts >= %(startTimestamp)s AND s.start_ts < %(endTimestamp)s" -# elif platform == 'desktop': -# sub_q = "AND s.user_device_type = 'desktop' AND s.project_id = %(project_id)s AND s.start_ts >= %(startTimestamp)s AND s.start_ts < %(endTimestamp)s" -# -# cur.execute(cur.mogrify(f"""\ -# SELECT s.project_id, -# s.session_id::text AS session_id, -# s.* -# FROM public.sessions AS s -# LEFT JOIN public.session_watchdogs AS sw ON s.session_id=sw.session_id -# LEFT JOIN public.watchdogs AS w ON w.watchdog_id=sw.watchdog_id -# WHERE s.project_id = %(project_id)s -# AND w.type='clickrage' -# AND s.start_ts>=%(startTimestamp)s -# AND s.start_ts<=%(endTimestamp)s -# {sub_q} -# ORDER BY s.session_id DESC -# LIMIT 5;""", -# {"project_id": project_id, "startTimestamp": startTimestamp, -# "endTimestamp": endTimestamp})) -# return helper.list_to_camel_case(cur.fetchall()) - - def get_missing_resources_trend(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): @@ -826,9 +785,8 @@ def get_missing_resources_trend(project_id, startTimestamp=TimeUTC.now(delta_day ch_sub_query += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - resources.url_hostpath AS key, - COUNT(resources.session_id) AS doc_count + ch_query = f"""SELECT resources.url_hostpath AS key, + COUNT(resources.session_id) AS doc_count FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY url_hostpath @@ -841,10 +799,9 @@ def get_missing_resources_trend(project_id, startTimestamp=TimeUTC.now(delta_day if len(rows) == 0: return [] ch_sub_query.append("resources.url_hostpath = %(value)s") - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COUNT(resources.session_id) AS doc_count, - toUnixTimestamp(MAX(resources.datetime))*1000 AS max_datatime + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + COUNT(resources.session_id) AS doc_count, + toUnixTimestamp(MAX(resources.datetime))*1000 AS max_datatime FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY timestamp @@ -879,9 +836,8 @@ def get_network(project_id, startTimestamp=TimeUTC.now(delta_days=-1), ch_sub_query_chart += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - resources.url_hostpath, COUNT(resources.session_id) AS doc_count + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + resources.url_hostpath, COUNT(resources.session_id) AS doc_count FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp, resources.url_hostpath @@ -935,9 +891,8 @@ def get_resources_loading_time(project_id, startTimestamp=TimeUTC.now(delta_days ch_sub_query_chart += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(resources.duration,0)) AS avg + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(resources.duration,0)) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -969,9 +924,8 @@ def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=- ch_sub_query_chart += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(pages.dom_building_time) AS avg + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(pages.dom_building_time) AS value FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -985,10 +939,10 @@ def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=- FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 - return {"avg": avg, + return {"value": avg, "chart": __complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, - density=density, neutral={"avg": 0})} + density=density, neutral={"value": 0})} def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1009,9 +963,8 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), ch_sub_query_chart.append("isNotNull(resources.duration)") ch_sub_query_chart.append("resources.duration>0") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - splitByChar('/', resources.url_hostpath)[-1] AS name, - AVG(NULLIF(resources.duration,0)) AS avg + ch_query = f"""SELECT splitByChar('/', resources.url_hostpath)[-1] AS name, + AVG(NULLIF(resources.duration,0)) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY name @@ -1030,9 +983,8 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), # r["url"] = r["url"].decode("utf-8") # except UnicodeDecodeError: # continue - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(resources.duration) AS avg + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(resources.duration) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -1119,7 +1071,7 @@ def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1 ch_sub_query_chart.append(f"url_path = %(value)s") with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - AVG(pages.response_time) AS avg + AVG(pages.response_time) AS value FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -1134,10 +1086,10 @@ def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1 FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 - return {"avg": avg, + return {"value": avg, "chart": __complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, - density=density, neutral={"avg": 0})} + density=density, neutral={"value": 0})} def get_pages_response_time_distribution(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1268,9 +1220,8 @@ def get_busiest_time_of_day(project_id, startTimestamp=TimeUTC.now(delta_days=-1 ch_sub_query += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - intDiv(toHour(sessions.datetime),2)*2 AS hour, - COUNT(sessions.session_id) AS count + ch_query = f"""SELECT intDiv(toHour(sessions.datetime),2)*2 AS hour, + COUNT(sessions.session_id) AS count FROM sessions {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY hour @@ -1320,7 +1271,7 @@ def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - AVG(pages.visually_complete) AS avg + AVG(pages.visually_complete) AS value FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -1334,9 +1285,9 @@ def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 - return {"avg": avg, "chart": __complete_missing_steps(rows=rows, start_time=startTimestamp, + return {"value": avg, "chart": __complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, - neutral={"avg": 0})} + neutral={"value": 0})} def get_impacted_sessions_by_slow_pages(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1353,7 +1304,7 @@ def get_impacted_sessions_by_slow_pages(project_id, startTimestamp=TimeUTC.now(d with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COUNT(DISTINCT pages.session_id) AS count + COUNT(DISTINCT pages.session_id) AS count FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} AND (pages.response_time)>(SELECT AVG(pages.response_time) @@ -1382,7 +1333,7 @@ def get_memory_consumption(project_id, startTimestamp=TimeUTC.now(delta_days=-1) with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(performance.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - AVG(performance.avg_used_js_heap_size) AS avg_used_js_heap_size + AVG(performance.avg_used_js_heap_size) AS value FROM performance {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -1396,11 +1347,11 @@ def get_memory_consumption(project_id, startTimestamp=TimeUTC.now(delta_days=-1) FROM performance {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 - return {"avgUsedJsHeapSize": avg, + return {"value": avg, "chart": helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, - neutral={"avg_used_js_heap_size": 0}))} + neutral={"value": 0}))} def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1413,7 +1364,7 @@ def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(performance.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - AVG(performance.avg_cpu) AS avg_cpu + AVG(performance.avg_cpu) AS value FROM performance {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -1427,11 +1378,11 @@ def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), FROM performance {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 - return {"avgCpu": avg, + return {"value": avg, "chart": helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, - neutral={"avg_cpu": 0}))} + neutral={"value": 0}))} def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1444,7 +1395,7 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(performance.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - AVG(performance.avg_fps) AS avg_fps + AVG(performance.avg_fps) AS value FROM performance {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -1458,11 +1409,11 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), FROM performance {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 - return {"avgFps": avg, + return {"value": avg, "chart": helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, - neutral={"avg_fps": 0}))} + neutral={"value": 0}))} def __get_crashed_sessions_ids(project_id, startTimestamp, endTimestamp): @@ -1698,9 +1649,8 @@ def get_slowest_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1), ch_sub_query += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - resources.url_host AS domain, - AVG(resources.duration) AS avg + ch_query = f"""SELECT resources.url_host AS domain, + AVG(resources.duration) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY resources.url_host @@ -1747,15 +1697,13 @@ def get_sessions_per_browser(project_id, startTimestamp=TimeUTC.now(delta_days=- ch_sub_query += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - b.user_browser AS browser, - b.count, - groupArray([bv.user_browser_version, toString(bv.count)]) AS versions + ch_query = f"""SELECT b.user_browser AS browser, + b.count, + groupArray([bv.user_browser_version, toString(bv.count)]) AS versions FROM ( - SELECT - sessions.user_browser, - COUNT(sessions.session_id) AS count + SELECT sessions.user_browser, + COUNT(sessions.session_id) AS count FROM sessions {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY sessions.user_browser @@ -1764,10 +1712,9 @@ def get_sessions_per_browser(project_id, startTimestamp=TimeUTC.now(delta_days=- ) AS b INNER JOIN ( - SELECT - sessions.user_browser, - sessions.user_browser_version, - COUNT(sessions.session_id) AS count + SELECT sessions.user_browser, + sessions.user_browser_version, + COUNT(sessions.session_id) AS count FROM sessions {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY @@ -1934,8 +1881,8 @@ def resource_type_vs_response_end(project_id, startTimestamp=TimeUTC.now(delta_d "endTimestamp": endTimestamp, **__get_constraint_values(args)} with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COUNT(resources.session_id) AS total, - SUM(if(resources.type='fetch',1,0)) AS xhr + COUNT(resources.session_id) AS total, + SUM(if(resources.type='fetch',1,0)) AS xhr FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -1946,7 +1893,7 @@ def resource_type_vs_response_end(project_id, startTimestamp=TimeUTC.now(delta_d density=density, neutral={"total": 0, "xhr": 0}) ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - AVG(pages.response_end) AS avg_response_end + AVG(pages.response_end) AS avg_response_end FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart_response_end)} GROUP BY timestamp @@ -1969,8 +1916,8 @@ def get_impacted_sessions_by_js_errors(project_id, startTimestamp=TimeUTC.now(de with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(errors.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COUNT(DISTINCT errors.session_id) AS sessions_count, - COUNT(DISTINCT errors.error_id) AS errors_count + COUNT(DISTINCT errors.session_id) AS sessions_count, + COUNT(DISTINCT errors.error_id) AS errors_count FROM errors {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY timestamp @@ -2008,15 +1955,13 @@ def get_resources_vs_visually_complete(project_id, startTimestamp=TimeUTC.now(de ch_sub_query_chart += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT - toUnixTimestamp(toStartOfInterval(s.base_datetime, toIntervalSecond(%(step_size)s))) * 1000 AS timestamp, - AVG(NULLIF(s.count,0)) AS avg, - groupArray([toString(t.type), toString(t.xavg)]) AS types + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(s.base_datetime, toIntervalSecond(%(step_size)s))) * 1000 AS timestamp, + AVG(NULLIF(s.count,0)) AS avg, + groupArray([toString(t.type), toString(t.xavg)]) AS types FROM - ( SELECT - resources.session_id, - MIN(resources.datetime) AS base_datetime, - COUNT(resources.url) AS count + ( SELECT resources.session_id, + MIN(resources.datetime) AS base_datetime, + COUNT(resources.url) AS count FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query_chart)} GROUP BY resources.session_id @@ -2137,3 +2082,490 @@ def get_resources_by_party(project_id, startTimestamp=TimeUTC.now(delta_days=-1) density=density, neutral={"first_party": 0, "third_party": 0})) + + +def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with ch_client.ClickHouseClient() as ch: + row = __get_application_activity_avg_page_load_time(ch, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + results["chart"] = get_performance_avg_page_load_time(project_id, startTimestamp, endTimestamp, **args) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + row = __get_application_activity_avg_page_load_time(ch, project_id, startTimestamp, endTimestamp, **args) + previous = helper.dict_to_camel_case(row) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_application_activity_avg_page_load_time(ch, project_id, startTimestamp, endTimestamp, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + ch_query = f"""\ + SELECT AVG(NULLIF(pages.load_event_end ,0)) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)};""" + params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + row = ch.execute(query=ch_query, params=params)[0] + result = row + for k in result: + if result[k] is None: + result[k] = 0 + return result + + +def get_performance_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), + density=19, resources=None, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + location_constraints = [] + meta_condition = __get_meta_constraint(args) + + location_constraints_vals = {} + + if resources and len(resources) > 0: + for r in resources: + if r["type"] == "LOCATION": + location_constraints.append(f"pages.url_path = %(val_{len(location_constraints)})s") + location_constraints_vals["val_" + str(len(location_constraints) - 1)] = r['value'] + + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + with ch_client.ClickHouseClient() as ch: + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, + data=args) + ch_sub_query_chart += meta_condition + + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(pages.load_event_end ,0)) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query_chart)} + {(f' AND ({" OR ".join(location_constraints)})') if len(location_constraints) > 0 else ""} + GROUP BY timestamp + ORDER BY timestamp;""" + + rows = ch.execute(query=ch_query, + params={**params, **location_constraints_vals, **__get_constraint_values(args)}) + pages = [{"timestamp": i["timestamp"], "value": i["value"]} for i in + __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, neutral={"value": 0})] + + for s in pages: + for k in s: + if s[k] is None: + s[k] = 0 + return pages + + +def get_application_activity_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with ch_client.ClickHouseClient() as ch: + row = __get_application_activity_avg_image_load_time(ch, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + results["chart"] = get_performance_avg_image_load_time(project_id, startTimestamp, endTimestamp, **args) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + row = __get_application_activity_avg_image_load_time(ch, project_id, startTimestamp, endTimestamp, **args) + previous = helper.dict_to_camel_case(row) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_application_activity_avg_image_load_time(ch, project_id, startTimestamp, endTimestamp, **args): + ch_sub_query = __get_basic_constraints(table_name="resources", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + ch_sub_query.append("resources.type= %(type)s") + ch_query = f"""\ + SELECT AVG(NULLIF(resources.duration,0)) AS value + FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)};""" + row = ch.execute(query=ch_query, + params={"project_id": project_id, "type": 'img', "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)})[0] + result = row + for k in result: + if result[k] is None: + result[k] = 0 + return result + + +def get_performance_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), + density=19, resources=None, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + img_constraints = [] + ch_sub_query_chart = __get_basic_constraints(table_name="resources", round_start=True, data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + img_constraints_vals = {} + + if resources and len(resources) > 0: + for r in resources: + if r["type"] == "IMG": + img_constraints.append(f"resources.url = %(val_{len(img_constraints)})s") + img_constraints_vals["val_" + str(len(img_constraints) - 1)] = r['value'] + + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + with ch_client.ClickHouseClient() as ch: + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(resources.duration,0)) AS value + FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query_chart)} + AND resources.type = 'img' + {(f' AND ({" OR ".join(img_constraints)})') if len(img_constraints) > 0 else ""} + GROUP BY timestamp + ORDER BY timestamp;""" + rows = ch.execute(query=ch_query, params={**params, **img_constraints_vals, **__get_constraint_values(args)}) + images = [{"timestamp": i["timestamp"], "value": i["value"]} for i in + __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, neutral={"value": 0})] + + for s in images: + for k in s: + if s[k] is None: + s[k] = 0 + return images + + +def get_application_activity_avg_request_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with ch_client.ClickHouseClient() as ch: + row = __get_application_activity_avg_request_load_time(ch, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + results["chart"] = get_performance_avg_request_load_time(project_id, startTimestamp, endTimestamp, **args) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + row = __get_application_activity_avg_request_load_time(ch, project_id, startTimestamp, endTimestamp, **args) + previous = helper.dict_to_camel_case(row) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_application_activity_avg_request_load_time(ch, project_id, startTimestamp, endTimestamp, **args): + ch_sub_query = __get_basic_constraints(table_name="resources", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + ch_sub_query.append("resources.type= %(type)s") + ch_query = f"""\ + SELECT AVG(NULLIF(resources.duration,0)) AS value + FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)};""" + row = ch.execute(query=ch_query, + params={"project_id": project_id, "type": 'fetch', "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)})[0] + result = row + for k in result: + if result[k] is None: + result[k] = 0 + return result + + +def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), + density=19, resources=None, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + request_constraints = [] + ch_sub_query_chart = __get_basic_constraints(table_name="resources", round_start=True, data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + request_constraints_vals = {} + + if resources and len(resources) > 0: + for r in resources: + if r["type"] != "IMG" and r["type"] == "LOCATION": + request_constraints.append(f"resources.url = %(val_{len(request_constraints)})s") + request_constraints_vals["val_" + str(len(request_constraints) - 1)] = r['value'] + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + with ch_client.ClickHouseClient() as ch: + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(resources.duration,0)) AS value + FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query_chart)} + AND resources.type = 'fetch' + {(f' AND ({" OR ".join(request_constraints)})') if len(request_constraints) > 0 else ""} + GROUP BY timestamp + ORDER BY timestamp;""" + rows = ch.execute(query=ch_query, + params={**params, **request_constraints_vals, **__get_constraint_values(args)}) + requests = [{"timestamp": i["timestamp"], "value": i["value"]} for i in + __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, density=density, + neutral={"value": 0})] + + for s in requests: + for k in s: + if s[k] is None: + s[k] = 0 + return requests + + +def get_page_metrics_avg_dom_content_load_start(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with ch_client.ClickHouseClient() as ch: + rows = __get_page_metrics_avg_dom_content_load_start(ch, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + results = helper.dict_to_camel_case(rows[0]) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + rows = __get_page_metrics_avg_dom_content_load_start(ch, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + previous = helper.dict_to_camel_case(rows[0]) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_page_metrics_avg_dom_content_load_start(ch, project_id, startTimestamp, endTimestamp, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + ch_query = f"""\ + SELECT COALESCE(AVG(NULLIF(pages.dom_content_loaded_event_end ,0)),0) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)};""" + params = {"project_id": project_id, "type": 'fetch', "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + rows = ch.execute(query=ch_query, params=params) + return rows + + +def get_page_metrics_avg_first_contentful_pixel(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + with ch_client.ClickHouseClient() as ch: + rows = __get_page_metrics_avg_first_contentful_pixel(ch, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + results = helper.dict_to_camel_case(rows[0]) + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + rows = __get_page_metrics_avg_first_contentful_pixel(ch, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + previous = helper.dict_to_camel_case(rows[0]) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_page_metrics_avg_first_contentful_pixel(ch, project_id, startTimestamp, endTimestamp, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + # changed dom_content_loaded_event_start to dom_content_loaded_event_end + ch_query = f"""\ + SELECT COALESCE(AVG(NULLIF(pages.first_contentful_paint,0)),0) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)};""" + params = {"project_id": project_id, "type": 'fetch', "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + rows = ch.execute(query=ch_query, params=params) + return rows + + +def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + results = {} + + with ch_client.ClickHouseClient() as ch: + rows = __get_user_activity_avg_visited_pages(ch, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + results = helper.dict_to_camel_case(rows[0]) + for key in results: + if isnan(results[key]): + results[key] = 0 + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + rows = __get_user_activity_avg_visited_pages(ch, project_id, startTimestamp, endTimestamp, **args) + + if len(rows) > 0: + previous = helper.dict_to_camel_case(rows[0]) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTimestamp, **args): + ch_sub_query = __get_basic_constraints(table_name="sessions", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + ch_query = f"""\ + SELECT COALESCE(CEIL(AVG(NULLIF(sessions.pages_count,0))),0) AS value + FROM sessions {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)};""" + params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + + rows = cur.execute(query=ch_query, params=params) + + return rows + + +def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), **args): + results = {} + + with ch_client.ClickHouseClient() as ch: + rows = __get_user_activity_avg_session_duration(ch, project_id, startTimestamp, endTimestamp, **args) + if len(rows) > 0: + results = helper.dict_to_camel_case(rows[0]) + for key in results: + if isnan(results[key]): + results[key] = 0 + diff = endTimestamp - startTimestamp + endTimestamp = startTimestamp + startTimestamp = endTimestamp - diff + rows = __get_user_activity_avg_session_duration(ch, project_id, startTimestamp, endTimestamp, **args) + + if len(rows) > 0: + previous = helper.dict_to_camel_case(rows[0]) + results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + return results + + +def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, endTimestamp, **args): + ch_sub_query = __get_basic_constraints(table_name="sessions", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + ch_query = f"""\ + SELECT COALESCE(AVG(NULLIF(sessions.duration,0)),0) AS value + FROM sessions {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)};""" + params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, + **__get_constraint_values(args)} + + rows = cur.execute(query=ch_query, params=params) + + return rows + + +def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + if value is not None: + ch_sub_query.append("pages.url_path = %(value)s") + with ch_client.ClickHouseClient() as ch: + ch_query = f"""SELECT COALESCE(AVG(pages.response_time),0) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)} AND isNotNull(pages.response_time) AND pages.response_time>0;""" + rows = ch.execute(query=ch_query, + params={"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)}) + return helper.dict_to_camel_case(rows[0]) + + +def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + if value is not None: + ch_sub_query.append("pages.url_path = %(value)s") + with ch_client.ClickHouseClient() as ch: + ch_query = f"""SELECT COUNT(pages.session_id) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)};""" + rows = ch.execute(query=ch_query, + params={"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)}) + return helper.dict_to_camel_case(rows[0]) + + +def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + if value is not None: + ch_sub_query.append("pages.url_path = %(value)s") + with ch_client.ClickHouseClient() as ch: + ch_query = f"""SELECT COALESCE(AVG(pages.first_paint),0) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)} AND isNotNull(pages.first_paint) AND pages.first_paint>0;""" + rows = ch.execute(query=ch_query, + params={"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)}) + return helper.dict_to_camel_case(rows[0]) + + +def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + if value is not None: + ch_sub_query.append("pages.url_path = %(value)s") + with ch_client.ClickHouseClient() as ch: + ch_query = f"""SELECT COALESCE(AVG(pages.dom_content_loaded_event_time),0) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)} AND isNotNull(pages.dom_content_loaded_event_time) AND pages.dom_content_loaded_event_time>0;""" + rows = ch.execute(query=ch_query, + params={"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)}) + return helper.dict_to_camel_case(rows[0]) + + +def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + if value is not None: + ch_sub_query.append("pages.url_path = %(value)s") + with ch_client.ClickHouseClient() as ch: + ch_query = f"""SELECT COALESCE(AVG(pages.ttfb),0) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)} AND isNotNull(pages.ttfb) AND pages.ttfb>0;""" + rows = ch.execute(query=ch_query, + params={"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)}) + return helper.dict_to_camel_case(rows[0]) + + +def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.now(delta_days=-1), + endTimestamp=TimeUTC.now(), value=None, **args): + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query += meta_condition + + if value is not None: + ch_sub_query.append("pages.url_path = %(value)s") + with ch_client.ClickHouseClient() as ch: + ch_query = f"""SELECT COALESCE(AVG(pages.time_to_interactive),0) AS value + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)} AND isNotNull(pages.time_to_interactive) AND pages.time_to_interactive >0;""" + rows = ch.execute(query=ch_query, + params={"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)}) + return helper.dict_to_camel_case(rows[0]) diff --git a/ee/api/requirements.txt b/ee/api/requirements.txt index ef143038b..5909d31c1 100644 --- a/ee/api/requirements.txt +++ b/ee/api/requirements.txt @@ -8,7 +8,7 @@ jira==3.1.1 clickhouse-driver==0.2.2 python3-saml==1.12.0 -fastapi==0.74.1 +fastapi==0.75.0 python-multipart==0.0.5 uvicorn[standard]==0.17.5 python-decouple==3.6 diff --git a/ee/api/routers/app/v1_api_ee.py b/ee/api/routers/subs/v1_api_ee.py similarity index 100% rename from ee/api/routers/app/v1_api_ee.py rename to ee/api/routers/subs/v1_api_ee.py From 2fe3f88a7d651b3d8e10cfc390f236c7b441994a Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 18:41:05 +0200 Subject: [PATCH 48/87] feat(api): EE metrics get slowest images optimized (from 30s to 4s) --- ee/api/chalicelib/core/dashboard.py | 47 +++++++++++++++++------------ 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index c834c852b..826be538a 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -456,7 +456,7 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), ch_sub_query.append("resources.type = 'img'") ch_sub_query_chart = __get_basic_constraints(table_name="resources", round_start=True, data=args) ch_sub_query_chart.append("resources.type = 'img'") - ch_sub_query_chart.append("resources.url = %(url)s") + ch_sub_query_chart.append("resources.url IN %(url)s") meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition ch_sub_query_chart += meta_condition @@ -468,35 +468,42 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY resources.url ORDER BY avg DESC LIMIT 10;""" - - rows = ch.execute(query=ch_query, - params={"project_id": project_id, "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, **__get_constraint_values(args)}) + params = {"project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)} + # print(ch.client().substitute_params(ch_query, params)) + rows = ch.execute(query=ch_query, params=params) rows = [{"url": i["url"], "avgDuration": i["avg"], "sessions": i["count"]} for i in rows] - + if len(rows) == 0: + return [] urls = [row["url"] for row in rows] charts = {} + ch_query = f"""\ + SELECT url, + toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(resources.duration,0)) AS avg + FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query_chart)} + GROUP BY url, timestamp + ORDER BY url, timestamp;""" + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, "url": urls, **__get_constraint_values(args)} + print(ch.client().substitute_params(ch_query, params)) + u_rows = ch.execute(query=ch_query, params=params) for url in urls: - ch_query = f"""\ - SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(resources.duration,0)) AS avg - FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} - WHERE {" AND ".join(ch_sub_query_chart)} - GROUP BY timestamp - ORDER BY timestamp;""" - params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, "url": url, **__get_constraint_values(args)} - r = ch.execute(query=ch_query, params=params) + sub_rows = [] + for r in u_rows: + if r["url"] == url: + sub_rows.append(r) charts[url] = [{"timestamp": int(i["timestamp"]), "avgDuration": i["avg"]} - for i in __complete_missing_steps(rows=r, start_time=startTimestamp, + for i in __complete_missing_steps(rows=sub_rows, start_time=startTimestamp, end_time=endTimestamp, density=density, neutral={"avg": 0})] for i in range(len(rows)): rows[i] = helper.dict_to_camel_case(rows[i]) - rows[i]["chart"] = [helper.dict_to_camel_case(chart) for chart in charts[rows[i]["url"]]] + rows[i]["chart"] = helper.list_to_camel_case(charts[rows[i]["url"]]) return sorted(rows, key=lambda k: k["sessions"], reverse=True) @@ -1286,8 +1293,8 @@ def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), WHERE {" AND ".join(ch_sub_query_chart)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 return {"value": avg, "chart": __complete_missing_steps(rows=rows, start_time=startTimestamp, - end_time=endTimestamp, density=density, - neutral={"value": 0})} + end_time=endTimestamp, density=density, + neutral={"value": 0})} def get_impacted_sessions_by_slow_pages(project_id, startTimestamp=TimeUTC.now(delta_days=-1), From 3d2ae11ce16a4f4a674e005cc3939d5ff650d8f9 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 18:48:42 +0200 Subject: [PATCH 49/87] feat(api): fixed get metrics with dashboards --- api/chalicelib/core/custom_metrics.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 6ca72f453..cb1e371c0 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -220,10 +220,11 @@ def get_all(project_id, user_id, include_series=False): {sub_join} LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards FROM (SELECT dashboard_id, name, is_public - FROM dashboards + FROM dashboards INNER JOIN dashboard_widgets USING (dashboard_id) WHERE deleted_at ISNULL + AND dashboard_widgets.metric_id = metrics.metric_id AND project_id = %(project_id)s - AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards + AND ((dashboards.user_id = %(user_id)s OR is_public))) AS connected_dashboards ) AS connected_dashboards ON (TRUE) LEFT JOIN LATERAL (SELECT email AS owner_email FROM users From 02209a7716b6ca057ca3903e087e60351724281e Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 18:53:41 +0200 Subject: [PATCH 50/87] feat(api): fixed update dashboard with metrics list --- api/chalicelib/core/dashboards2.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 3efd1e968..dd259a077 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -113,8 +113,7 @@ def update_dashboard(project_id, user_id, dashboard_id, data: schemas.EditDashbo if data.metrics is not None and len(data.metrics) > 0: pg_query = f"""WITH dash AS ({pg_query}) INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config) - VALUES {",".join([f"(%(dashboard_id)s, %(metric_id_{i})s, %(userId)s, %(config_{i})s)" for i in range(len(data.metrics))])} - RETURNING (SELECT dashboard_id FROM dash)""" + VALUES {",".join([f"(%(dashboard_id)s, %(metric_id_{i})s, %(userId)s, %(config_{i})s)" for i in range(len(data.metrics))])};""" for i, m in enumerate(data.metrics): params[f"metric_id_{i}"] = m params[f"config_{i}"] = schemas.AddWidgetToDashboardPayloadSchema.schema() \ From 5582f0ac36a7ed3522ec14725f277aa25674471f Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 19:18:29 +0200 Subject: [PATCH 51/87] feat(api): fixed get dashboard with templates --- api/chalicelib/core/dashboards2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index dd259a077..0e233d54b 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -76,7 +76,7 @@ def get_dashboard(project_id, user_id, dashboard_id): ) AS metric_series ON (TRUE) WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id AND metrics.deleted_at ISNULL - AND metrics.project_id = %(projectId)s) AS raw_metrics + AND (metrics.project_id = %(projectId)s OR metrics.project_id ISNULL)) AS raw_metrics ) AS all_metric_widgets ON (TRUE) WHERE dashboards.deleted_at ISNULL AND dashboards.project_id = %(projectId)s From ef6ba3b9ce83201a88e3c4e56c799e1361cfa0c0 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 19:40:07 +0200 Subject: [PATCH 52/87] feat(api): EE metrics get slowest resources optimized (from 52s to 7s) --- ee/api/chalicelib/core/dashboard.py | 72 ++++++++++++++--------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 826be538a..9c02e5ae4 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -489,13 +489,15 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), ORDER BY url, timestamp;""" params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, "url": urls, **__get_constraint_values(args)} - print(ch.client().substitute_params(ch_query, params)) + # print(ch.client().substitute_params(ch_query, params)) u_rows = ch.execute(query=ch_query, params=params) for url in urls: sub_rows = [] for r in u_rows: if r["url"] == url: sub_rows.append(r) + elif len(sub_rows) > 0: + break charts[url] = [{"timestamp": int(i["timestamp"]), "avgDuration": i["avg"]} for i in __complete_missing_steps(rows=sub_rows, start_time=startTimestamp, @@ -970,52 +972,50 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), ch_sub_query_chart.append("isNotNull(resources.duration)") ch_sub_query_chart.append("resources.duration>0") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT splitByChar('/', resources.url_hostpath)[-1] AS name, + ch_query = f"""SELECT any(url) AS url, any(type) AS type, + splitByChar('/', resources.url_hostpath)[-1] AS name, AVG(NULLIF(resources.duration,0)) AS avg FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY name ORDER BY avg DESC LIMIT 10;""" - rows = ch.execute(query=ch_query, - params={"project_id": project_id, - "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, **__get_constraint_values(args)}) - ch_sub_query_chart.append("endsWith(resources.url_hostpath, %(url)s)>0") + params = {"project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)} + print(ch.format(query=ch_query, params=params)) + rows = ch.execute(query=ch_query, params=params) + # ch_sub_query_chart.append("endsWith(resources.url_hostpath, %(url)s)>0") ch_sub_query.append(ch_sub_query_chart[-1]) results = [] + names = {f"name_{i}": r["name"] for i, r in enumerate(rows)} + ch_query = f"""SELECT splitByChar('/', resources.url_hostpath)[-1] AS name, + toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(resources.duration) AS avg + FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query_chart)} + AND ({" OR ".join([f"endsWith(resources.url_hostpath, %(name_{i})s)>0" for i in range(len(names.keys()))])}) + GROUP BY name,timestamp + ORDER BY name,timestamp;""" + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + **names, **__get_constraint_values(args)} + # print(ch.format(query=ch_query, params=params)) + charts = ch.execute(query=ch_query, params=params) for r in rows: - # if isinstance(r["url"], bytes): - # try: - # r["url"] = r["url"].decode("utf-8") - # except UnicodeDecodeError: - # continue - ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(resources.duration) AS avg - FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} - WHERE {" AND ".join(ch_sub_query_chart)} - GROUP BY timestamp - ORDER BY timestamp;""" - chart = ch.execute(query=ch_query, - params={"step_size": step_size, "project_id": project_id, - "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, - "url": r["name"], **__get_constraint_values(args)}) - r["chart"] = __complete_missing_steps(rows=chart, start_time=startTimestamp, + sub_chart = [] + for c in charts: + if c["name"] == r["name"]: + cc = dict(c) + cc.pop("name") + sub_chart.append(cc) + elif len(sub_chart) > 0: + break + r["chart"] = __complete_missing_steps(rows=sub_chart, start_time=startTimestamp, end_time=endTimestamp, density=density, neutral={"avg": 0}) - ch_query = f"""SELECT url, type - FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} - WHERE {" AND ".join(ch_sub_query)} - ORDER BY duration DESC - LIMIT 1;""" - url = ch.execute(query=ch_query, - params={"project_id": project_id, - "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, - "url": r["name"], **__get_constraint_values(args)}) - r["url"] = url[0]["url"] - r["type"] = __get_resource_type_from_db_type(url[0]["type"]) + r["type"] = __get_resource_type_from_db_type(r["type"]) results.append(r) return results From 3f621165d6bb30a7ff3ae50b63dd348a60fcfc7d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 19:43:44 +0200 Subject: [PATCH 53/87] feat(api): EE metrics refactored --- ee/api/chalicelib/core/dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 9c02e5ae4..c36c877da 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -985,7 +985,7 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "endTimestamp": endTimestamp, **__get_constraint_values(args)} print(ch.format(query=ch_query, params=params)) rows = ch.execute(query=ch_query, params=params) - # ch_sub_query_chart.append("endsWith(resources.url_hostpath, %(url)s)>0") + ch_sub_query.append(ch_sub_query_chart[-1]) results = [] names = {f"name_{i}": r["name"] for i, r in enumerate(rows)} From 07fd1027f8ab2b7b7f9b67b96650aad160a81480 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 19:55:00 +0200 Subject: [PATCH 54/87] feat(api): EE new method for debug in ch_client --- ee/api/chalicelib/utils/ch_client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ee/api/chalicelib/utils/ch_client.py b/ee/api/chalicelib/utils/ch_client.py index babdd669a..aa45699f7 100644 --- a/ee/api/chalicelib/utils/ch_client.py +++ b/ee/api/chalicelib/utils/ch_client.py @@ -25,5 +25,8 @@ class ClickHouseClient: def client(self): return self.__client + def format(self, query, params): + return self.__client.substitute_params(query, params) + def __exit__(self, *args): pass From 584de8d09c5a329ca3a49e70c4a703f71e42f0a7 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 11:10:02 +0200 Subject: [PATCH 55/87] feat(api): jira return Auth-error to UI feat(api): metrics allow empty series --- api/chalicelib/utils/jira_client.py | 1 + api/schemas.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/chalicelib/utils/jira_client.py b/api/chalicelib/utils/jira_client.py index 8370feb5e..40bc0d66c 100644 --- a/api/chalicelib/utils/jira_client.py +++ b/api/chalicelib/utils/jira_client.py @@ -22,6 +22,7 @@ class JiraManager: except Exception as e: print("!!! JIRA AUTH ERROR") print(e) + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: Authentication error") def set_jira_project_id(self, project_id): self._config["JIRA_PROJECT_ID"] = project_id diff --git a/api/schemas.py b/api/schemas.py index 1f1bec739..989ece66e 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -820,7 +820,7 @@ class CustomMetricChartPayloadSchema(CustomMetricSessionsPayloadSchema): class CreateCustomMetricsSchema(CustomMetricChartPayloadSchema): name: str = Field(...) - series: List[CustomMetricCreateSeriesSchema] = Field(..., min_items=1) + series: List[CustomMetricCreateSeriesSchema] = Field(...) is_public: bool = Field(default=True) view_type: Union[MetricTimeseriesViewType, MetricTableViewType] = Field(MetricTimeseriesViewType.line_chart) metric_type: MetricType = Field(MetricType.timeseries) From 3c30ebf76d800d661534f43a68fbbc38ae2f6232 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 14:17:24 +0200 Subject: [PATCH 56/87] feat(api): changed update widget feat(api): FOSS&EE optimized /projects --- api/chalicelib/core/dashboards2.py | 2 +- api/chalicelib/core/projects.py | 25 ++++++++++++------------- api/routers/subs/metrics.py | 2 +- api/schemas.py | 10 ++++++++-- ee/api/.gitignore | 1 + ee/api/chalicelib/core/projects.py | 22 +++++++++++----------- 6 files changed, 34 insertions(+), 28 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 0e233d54b..afe65d71a 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -167,7 +167,7 @@ def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashb return helper.dict_to_camel_case(row) -def update_widget(project_id, user_id, dashboard_id, widget_id, data: schemas.AddWidgetToDashboardPayloadSchema): +def update_widget(project_id, user_id, dashboard_id, widget_id, data: schemas.UpdateWidgetPayloadSchema): with pg_client.PostgresClient() as cur: pg_query = """UPDATE dashboard_widgets SET config= %(config)s diff --git a/api/chalicelib/core/projects.py b/api/chalicelib/core/projects.py index 0c2fd9e01..3559f645a 100644 --- a/api/chalicelib/core/projects.py +++ b/api/chalicelib/core/projects.py @@ -65,27 +65,26 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st FROM public.projects AS s {'LEFT JOIN LATERAL (SELECT COUNT(*) AS count FROM public.integrations WHERE s.project_id = integrations.project_id LIMIT 1) AS stack_integrations ON TRUE' if stack_integrations else ''} WHERE s.deleted_at IS NULL - ORDER BY s.project_id;""" - ) + ORDER BY s.project_id;""") rows = cur.fetchall() if recording_state: project_ids = [f'({r["project_id"]})' for r in rows] - query = f"""SELECT projects.project_id, COALESCE(MAX(start_ts), 0) AS last - FROM (VALUES {",".join(project_ids)}) AS projects(project_id) - LEFT JOIN sessions USING (project_id) - GROUP BY project_id;""" - cur.execute( - query=query - ) + query = cur.mogrify(f"""SELECT projects.project_id, COALESCE(MAX(start_ts), 0) AS last + FROM (VALUES {",".join(project_ids)}) AS projects(project_id) + LEFT JOIN sessions USING (project_id) + WHERE sessions.start_ts >= %(startDate)s AND sessions.start_ts <= %(endDate)s + GROUP BY project_id;""", + {"startDate": TimeUTC.now(delta_days=-3), "endDate": TimeUTC.now(delta_days=1)}) + + cur.execute(query=query) status = cur.fetchall() for r in rows: + r["status"] = "red" for s in status: if s["project_id"] == r["project_id"]: - if s["last"] < TimeUTC.now(-2): - r["status"] = "red" - elif s["last"] < TimeUTC.now(-1): + if TimeUTC.now(-2) <= s["last"] < TimeUTC.now(-1): r["status"] = "yellow" - else: + elif s["last"] >= TimeUTC.now(-1): r["status"] = "green" break diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index fc875e0fd..02eec811d 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -68,7 +68,7 @@ def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int, @app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) @app.put('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) def update_widget_in_dashboard(projectId: int, dashboardId: int, widgetId: int, - data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), + data: schemas.UpdateWidgetPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return dashboards2.update_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, widget_id=widgetId, data=data) diff --git a/api/schemas.py b/api/schemas.py index 989ece66e..65db116c0 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -893,8 +893,7 @@ class EditDashboardSchema(CreateDashboardSchema): is_pinned: Optional[bool] = Field(default=None) -class AddWidgetToDashboardPayloadSchema(BaseModel): - metric_id: int = Field(default=None) +class UpdateWidgetPayloadSchema(BaseModel): # if you change the config attribute name, please make sure to update it in dashboard2.py config: dict = Field(default={"col": 1, "row": 1, "position": 0}) @@ -902,6 +901,13 @@ class AddWidgetToDashboardPayloadSchema(BaseModel): alias_generator = attribute_to_camel_case +class AddWidgetToDashboardPayloadSchema(UpdateWidgetPayloadSchema): + metric_id: int = Field(default=None) + + class Config: + alias_generator = attribute_to_camel_case + + # these values should match the keys in metrics table class TemplateKeys(str, Enum): count_sessions = "count_sessions" diff --git a/ee/api/.gitignore b/ee/api/.gitignore index af91e1919..488fab072 100644 --- a/ee/api/.gitignore +++ b/ee/api/.gitignore @@ -259,3 +259,4 @@ Pipfile /build_alerts.sh /routers/subs/metrics.py /routers/subs/v1_api.py +/chalicelib/core/dashboards2.py diff --git a/ee/api/chalicelib/core/projects.py b/ee/api/chalicelib/core/projects.py index 0255c8c8c..3072f55a0 100644 --- a/ee/api/chalicelib/core/projects.py +++ b/ee/api/chalicelib/core/projects.py @@ -82,22 +82,22 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st rows = cur.fetchall() if recording_state: project_ids = [f'({r["project_id"]})' for r in rows] - query = f"""SELECT projects.project_id, COALESCE(MAX(start_ts), 0) AS last - FROM (VALUES {",".join(project_ids)}) AS projects(project_id) - LEFT JOIN sessions USING (project_id) - GROUP BY project_id;""" - cur.execute( - query=query - ) + query = cur.mogrify(f"""SELECT projects.project_id, COALESCE(MAX(start_ts), 0) AS last + FROM (VALUES {",".join(project_ids)}) AS projects(project_id) + LEFT JOIN sessions USING (project_id) + WHERE sessions.start_ts >= %(startDate)s AND sessions.start_ts <= %(endDate)s + GROUP BY project_id;""", + {"startDate": TimeUTC.now(delta_days=-3), "endDate": TimeUTC.now(delta_days=1)}) + + cur.execute(query=query) status = cur.fetchall() for r in rows: + r["status"] = "red" for s in status: if s["project_id"] == r["project_id"]: - if s["last"] < TimeUTC.now(-2): - r["status"] = "red" - elif s["last"] < TimeUTC.now(-1): + if TimeUTC.now(-2) <= s["last"] < TimeUTC.now(-1): r["status"] = "yellow" - else: + elif s["last"] >= TimeUTC.now(-1): r["status"] = "green" break From 70ef8af0cb81ff507ed2517c5cc8b60308f72166 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 14:25:43 +0200 Subject: [PATCH 57/87] feat(api): fixed duplicate dashboard for metrics list --- api/chalicelib/core/custom_metrics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index cb1e371c0..77f902dcb 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -219,7 +219,7 @@ def get_all(project_id, user_id, include_series=False): FROM metrics {sub_join} LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards - FROM (SELECT dashboard_id, name, is_public + FROM (SELECT DISTINCT dashboard_id, name, is_public FROM dashboards INNER JOIN dashboard_widgets USING (dashboard_id) WHERE deleted_at ISNULL AND dashboard_widgets.metric_id = metrics.metric_id From caa58d1f98be5f8228473f072b19d88216fd6bb6 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 6 Apr 2022 14:46:28 +0200 Subject: [PATCH 58/87] feat(ui) - dashboard - wip --- frontend/app/Router.js | 2 + .../BugFinder/AutoComplete/AutoComplete.js | 2 +- .../DashboardEditModal/DashboardEditModal.tsx | 86 +++++++++++++++++++ .../components/DashboardEditModal/index.ts | 1 + .../DashboardMetricSelection.tsx | 4 +- .../DashboardModal/DashboardModal.tsx | 2 +- .../DashboardRouter/DashboardRouter.tsx | 7 +- .../DashboardView/DashboardView.tsx | 37 ++++++-- .../DashboardWidgetGrid.tsx | 4 +- .../MetricListItem/MetricListItem.tsx | 14 +-- .../components/MetricsList/MetricsList.tsx | 12 +-- .../components/MetricsView/MetricsView.tsx | 10 ++- .../components/WidgetChart/WidgetChart.tsx | 6 +- .../components/WidgetForm/WidgetForm.tsx | 13 ++- .../components/WidgetName/WidgetName.tsx | 57 ++++++++++++ .../Dashboard/components/WidgetName/index.ts | 1 + .../WidgetPreview/WidgetPreview.tsx | 4 +- .../components/WidgetView/WidgetView.tsx | 7 +- .../WidgetWrapper/WidgetWrapper.tsx | 57 +++++++----- .../app/components/ui/ItemMenu/ItemMenu.js | 45 +++------- frontend/app/mstore/dashboardStore.ts | 16 +++- frontend/app/mstore/metricStore.ts | 3 + frontend/app/mstore/types/dashboard.ts | 7 +- frontend/app/mstore/types/widget.ts | 5 ++ frontend/app/routes.js | 2 +- 25 files changed, 310 insertions(+), 94 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/DashboardEditModal/DashboardEditModal.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardEditModal/index.ts create mode 100644 frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx create mode 100644 frontend/app/components/Dashboard/components/WidgetName/index.ts diff --git a/frontend/app/Router.js b/frontend/app/Router.js index 22b5b3947..f42f3c456 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -59,6 +59,7 @@ const METRICS_DETAILS = routes.metricDetails(); const DASHBOARD_PATH = routes.dashboard(); const DASHBOARD_SELECT_PATH = routes.dashboardSelected(); const DASHBOARD_METRIC_CREATE_PATH = routes.dashboardMetricCreate(); +const DASHBOARD_METRIC_DETAILS_PATH = routes.dashboardMetricDetails(); // const WIDGET_PATAH = routes.dashboardMetric(); const SESSIONS_PATH = routes.sessions(); @@ -206,6 +207,7 @@ class Router extends React.Component { + diff --git a/frontend/app/components/BugFinder/AutoComplete/AutoComplete.js b/frontend/app/components/BugFinder/AutoComplete/AutoComplete.js index 6b90786b7..b528e7bb9 100644 --- a/frontend/app/components/BugFinder/AutoComplete/AutoComplete.js +++ b/frontend/app/components/BugFinder/AutoComplete/AutoComplete.js @@ -114,7 +114,7 @@ class AutoComplete extends React.PureComponent { render() { const { ddOpen, query, loading, values } = this.state; - const { + const { optionMapping = defaultOptionMapping, valueToText = defaultValueToText, placeholder = 'Type to search...', diff --git a/frontend/app/components/Dashboard/components/DashboardEditModal/DashboardEditModal.tsx b/frontend/app/components/Dashboard/components/DashboardEditModal/DashboardEditModal.tsx new file mode 100644 index 000000000..e6eae9e4e --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardEditModal/DashboardEditModal.tsx @@ -0,0 +1,86 @@ +import { useObserver } from 'mobx-react-lite'; +import React from 'react'; +import { Button, Modal, Form, Icon, Checkbox } from 'UI'; +import { useStore } from 'App/mstore' + +interface Props { + show: boolean; + // dashboard: any; + closeHandler?: () => void; +} +function DashboardEditModal(props: Props) { + const { show, closeHandler } = props; + const { dashboardStore } = useStore(); + const dashboard = useObserver(() => dashboardStore.dashboardInstance); + + const onSave = () => { + dashboardStore.save(dashboard).then(closeHandler); + } + + const write = ({ target: { value, name } }) => dashboard.update({ [ name ]: value }) + const writeOption = (e, { checked, name }) => { + dashboard.update({ [name]: checked }); + } + + return useObserver(() => ( + + +
{ 'Edit Dashboard' }
+ +
+ + +
+ + + + + + +
+ +
dashboard.update({ 'isPublic': !dashboard.isPublic }) }> + + Team can see and edit the dashboard. +
+
+
+
+
+ +
+ + +
+
+
+ )); +} + +export default DashboardEditModal; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardEditModal/index.ts b/frontend/app/components/Dashboard/components/DashboardEditModal/index.ts new file mode 100644 index 000000000..c7f4d7b17 --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardEditModal/index.ts @@ -0,0 +1 @@ +export { default } from './DashboardEditModal' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index 78f50a45e..475aeaa6d 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -91,10 +91,10 @@ function DashboardMetricSelection(props) { {activeCategory && activeCategory.widgets.map((widget: any) => (
dashboardStore.toggleWidgetSelection(widget)} > - +
))}
diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 07be9fb84..a5ba99dfb 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -34,7 +34,7 @@ function DashboardModal(props) { return useObserver(() => (
diff --git a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx index 171a36164..9621f1c71 100644 --- a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx +++ b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx @@ -7,6 +7,7 @@ import { metricDetails, dashboardSelected, dashboardMetricCreate, + dashboardMetricDetails, withSiteId, dashboard, } from 'App/routes'; @@ -24,13 +25,17 @@ function DashboardRouter(props: Props) {
- + + + + + diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index 2b9f9db98..f3dfdabcc 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react'; import { observer, useObserver } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; -import { Button, PageTitle, Link, Loader, NoContent } from 'UI'; +import { Button, PageTitle, Link, Loader, NoContent, ItemMenu } from 'UI'; import { withSiteId, dashboardMetricCreate, dashboardSelected, dashboard } from 'App/routes'; import withModal from 'App/components/Modal/withModal'; import DashboardWidgetGrid from '../DashboardWidgetGrid'; @@ -9,6 +9,7 @@ import { confirm } from 'UI/Confirmation'; import { withRouter } from 'react-router-dom'; import { useModal } from 'App/components/Modal'; import DashboardModal from '../DashboardModal'; +import DashboardEditModal from '../DashboardEditModal'; interface Props { siteId: number; @@ -22,16 +23,22 @@ function DashboardView(props: Props) { const { hideModal, showModal } = useModal(); const loading = useObserver(() => dashboardStore.fetchingDashboard); const dashboard: any = dashboardStore.selectedDashboard + const [showEditModal, setShowEditModal] = React.useState(false); useEffect(() => { dashboardStore.fetch(dashboardId) }, []); - const onEditHandler = () => { + const onAddWidgets = () => { dashboardStore.initDashboard(dashboard) showModal(, {}) } + const onEdit = () => { + dashboardStore.initDashboard(dashboard) + setShowEditModal(true) + } + const onDelete = async () => { if (await confirm({ header: 'Confirm', @@ -54,17 +61,37 @@ function DashboardView(props: Props) { size="small" >
+ setShowEditModal(false)} + />
{/* */} - +
- +
- +
diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx index 9bb7a1ce5..77356a279 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx @@ -5,11 +5,12 @@ import { NoContent, Button, Loader } from 'UI'; import { useObserver } from 'mobx-react-lite'; interface Props { + siteId: string, dashboardId: string; onEditHandler: () => void; } function DashboardWidgetGrid(props) { - const { dashboardId } = props; + const { dashboardId, siteId } = props; const { dashboardStore } = useStore(); const loading = useObserver(() => dashboardStore.isLoading); const dashbaord: any = dashboardStore.selectedDashboard; @@ -36,6 +37,7 @@ function DashboardWidgetGrid(props) { key={item.widgetId} moveListItem={(dragIndex, hoverIndex) => dashbaord.swapWidgetPosition(dragIndex, hoverIndex)} dashboardId={dashboardId} + siteId={siteId} /> ))}
diff --git a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx index b8e623370..f9f5e6d96 100644 --- a/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx +++ b/frontend/app/components/Dashboard/components/MetricListItem/MetricListItem.tsx @@ -10,9 +10,9 @@ function DashboardLink({ dashboards}) { return ( dashboards.map(dashboard => ( -
+
·
- {dashboard.name} + {dashboard.name}
)) @@ -22,24 +22,24 @@ function DashboardLink({ dashboards}) { function MetricListItem(props: Props) { const { metric } = props; return ( -
-
+
+
{metric.name}
-
+
-
{metric.owner}
+
{metric.owner}
{metric.isPublic ? 'Team' : 'Private'}
-
{metric.lastModified && checkForRecent(metric.lastModified, 'LLL dd, yyyy, hh:mm a')}
+
{metric.lastModified && checkForRecent(metric.lastModified, 'LLL dd, yyyy, hh:mm a')}
); } diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index 729844590..dd1af190b 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -19,13 +19,13 @@ function MetricsList(props: Props) { return useObserver(() => (
-
-
Title
+
+
Title
Type
-
Dashboards
-
Owner
-
Visibility & Edit Access
-
Last Modified
+
Dashboards
+
Owner
+
Visibility
+
Last Modified
{list.map((metric: any) => ( diff --git a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx index 18c1204bb..c6f33fb39 100644 --- a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx @@ -1,12 +1,16 @@ import React from 'react'; import { Button, PageTitle, Icon, Link } from 'UI'; -import { withSiteId, dashboardMetricCreate } from 'App/routes'; +import { withSiteId, metricCreate } from 'App/routes'; import MetricsList from '../MetricsList'; import MetricsSearch from '../MetricsSearch'; import { useStore } from 'App/mstore'; import { useObserver } from 'mobx-react-lite'; -function MetricsView(props) { +interface Props{ + siteId: number; +} +function MetricsView(props: Props) { + const { siteId } = props; const { metricStore } = useStore(); React.useEffect(() => { @@ -16,7 +20,7 @@ function MetricsView(props) {
- {/* */} +
diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index 2dd27bc9e..b20c3a1b4 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -11,12 +11,12 @@ import { observer, useObserver } from 'mobx-react-lite'; import { Loader } from 'UI'; import { useStore } from 'App/mstore'; interface Props { - // metric: any; + metric: any; } function WidgetChart(props: Props) { - // const metric = useObserver(() => props.metric); + const metric = useObserver(() => props.metric); const { metricStore } = useStore(); - const metric: any = useObserver(() => metricStore.instance); + // const metric: any = useObserver(() => metricStore.instance); const series = useObserver(() => metric.series); const colors = Styles.customMetricColors; const [loading, setLoading] = useState(false) diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 05a2ec910..abe92223a 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -8,6 +8,7 @@ import { HelpText, Button, Icon } from 'UI' import FilterSeries from '../FilterSeries'; import { withRouter } from 'react-router-dom'; import { confirm } from 'UI/Confirmation'; +import { withSiteId, dashboardMetricDetails, metricDetails } from 'App/routes' interface Props { history: any; @@ -50,7 +51,17 @@ function WidgetForm(props: Props) { }; const onSave = () => { - metricStore.save(metric, dashboardId); + const wasCreating = !metric.exists() + metricStore.save(metric, dashboardId).then(() => { + if (wasCreating) { + if (parseInt(dashboardId) > 0) { + history.push(withSiteId(dashboardMetricDetails(parseInt(dashboardId)), siteId)); + } else { + history.push(withSiteId(metricDetails(metric.metricId), siteId)); + } + + } + }); } const onDelete = async () => { diff --git a/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx b/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx new file mode 100644 index 000000000..0cbfafd49 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx @@ -0,0 +1,57 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Icon } from 'UI'; + +interface Props { + name: string; + onUpdate: (name) => void; + seriesIndex?: number; +} +function WidgetName(props: Props) { + const { seriesIndex = 1 } = props; + const [editing, setEditing] = useState(false) + const [name, setName] = useState(props.name) + const ref = useRef(null) + + const write = ({ target: { value, name } }) => { + setName(value) + } + + const onBlur = () => { + setEditing(false) + props.onUpdate(name.trim() === '' ? 'New Widget' : name) + } + + useEffect(() => { + if (editing) { + ref.current.focus() + } + }, [editing]) + + useEffect(() => { + setName(props.name) + }, [props.name]) + + // const { name } = props; + return ( +
+ { editing ? ( + setEditing(true)} + /> + ) : ( +
{ name }
+ )} + +
setEditing(true)}>
+
+ ); +} + +export default WidgetName; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetName/index.ts b/frontend/app/components/Dashboard/components/WidgetName/index.ts new file mode 100644 index 000000000..322cc4441 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetName/index.ts @@ -0,0 +1 @@ +export { default } from './WidgetName'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index a227fce93..2c40dd0f0 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -25,6 +25,8 @@ function WidgetPreview(props: Props) { metric.update({ ...changedDates, rangeName: changedDates.rangeValue }); } + console.log('view', metric.viewType) + return useObserver(() => (
@@ -77,7 +79,7 @@ function WidgetPreview(props: Props) { />
-
+
diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index a63c58beb..f86e22302 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -7,6 +7,7 @@ import WidgetSessions from '../WidgetSessions'; import { Icon, BackLink, Loader } from 'UI'; import { useObserver } from 'mobx-react-lite'; import { withSiteId } from 'App/routes'; +import WidgetName from '../WidgetName'; interface Props { history: any; match: any @@ -32,7 +33,7 @@ function WidgetView(props: Props) { const onBackHandler = () => { if (dashboardId) { props.history.push(withSiteId(`/dashboard/${dashboardId}`, siteId)); - } { + } else { props.history.push(withSiteId(`/metrics`, siteId)); } } @@ -43,7 +44,9 @@ function WidgetView(props: Props) {
-

{widget.name}

+

+ metricStore.merge({ name })} /> +

setExpanded(!expanded)} diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index f426dac6e..c667ae171 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -6,6 +6,9 @@ import WidgetChart from '../WidgetChart'; import { useObserver } from 'mobx-react-lite'; import { confirm } from 'UI/Confirmation'; import { useStore } from 'App/mstore'; +import LazyLoad from 'react-lazyload'; +import { withRouter } from 'react-router-dom'; +import { withSiteId, dashboardMetricDetails } from 'App/routes'; interface Props { className?: string; @@ -13,11 +16,15 @@ interface Props { index?: number; moveListItem?: any; isPreview?: boolean; + isTemplate?: boolean dashboardId?: string; + siteId?: string, + active?: boolean; + history?: any } function WidgetWrapper(props: Props) { const { dashboardStore } = useStore(); - const { widget, index = 0, moveListItem = null, isPreview = false, dashboardId } = props; + const { active = false, widget, index = 0, moveListItem = null, isPreview = false, isTemplate = false, dashboardId, siteId } = props; const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -47,54 +54,62 @@ function WidgetWrapper(props: Props) { confirmButton: 'Yes, Delete', confirmation: `Are you sure you want to permanently delete this Dashboard?` })) { - dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId).then(() => { - - }) + dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); } } + const editHandler = () => { + console.log('clicked', widget.metricId); + } + + const onChartClick = () => { + if (isPreview || isTemplate) return; + props.history.push(withSiteId(dashboardMetricDetails(dashboardId, widget.metricId),siteId)); + } + const ref: any = useRef(null) const dragDropRef: any = dragRef(dropRef(ref)) return useObserver(() => (
- {/* */} -
- {widget.name} +
+ +

{widget.name}

+ {!isPreview && !isTemplate && (
{ - console.log('edit'); - } + text: 'Edit', onClick: editHandler, }, { - text: 'Hide from view' + dashboardId, + text: 'Hide from view', onClick: onDelete }, ]} />
-
+ )} +
-
- + +
+
- {/* */} +
)); } -export default WidgetWrapper; \ No newline at end of file +export default withRouter(WidgetWrapper); \ No newline at end of file diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.js b/frontend/app/components/ui/ItemMenu/ItemMenu.js index 1d908c33e..1938f5a3c 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.js +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.js @@ -1,35 +1,18 @@ import { Icon } from 'UI'; import styles from './itemMenu.css'; +import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv'; export default class ItemMenu extends React.PureComponent { state = { displayed: false, }; - componentDidMount() { - document.addEventListener('click', this.handleClickOutside); - } - - componentWillUnmount() { - document.removeEventListener('click', this.handleClickOutside); - } - onClick = callback => (e) => { e.stopPropagation(); callback(e); } - handleClickOutside = (e) => { - if (!this.state.displayed) return; - if (e.target !== this.menuBtnRef) { - this.closeMenu(); - } - } - toggleMenu = (e) => { - // e.preventDefault(); - // e.stopPropagation(); - console.log('toggleMenu', e); this.setState({ displayed: !this.state.displayed }); } @@ -41,22 +24,18 @@ export default class ItemMenu extends React.PureComponent { return (
- {/*
{ this.menuBtnRef = ref; } } - className={ styles.menuBtn } - onClick={ this.toggleMenu } - role="button" - tabIndex="-1" - /> */} -
{ this.menuBtnRef = ref; } } - className="w-10 h-10 cursor-pointer bg-white rounded-full flex items-center justify-center hover:bg-gray-lightest" - onClick={ this.toggleMenu } - role="button" - // tabIndex="-1" + - -
+
{ this.menuBtnRef = ref; } } + className="w-10 h-10 cursor-pointer bg-white rounded-full flex items-center justify-center hover:bg-gray-lightest" + onClick={ this.toggleMenu } + role="button" + > + +
+
toJson(): void fromJson(json: any): void - initDashboard(dashboard: IDashboard): void + // initDashboard(dashboard: IDashboard): void addDashboard(dashboard: IDashboard): void removeDashboard(dashboard: IDashboard): void getDashboard(dashboardId: string): void @@ -78,6 +79,7 @@ export default class DashboardStore implements IDashboardSotre { constructor() { makeAutoObservable(this, { widgetCategories: observable.ref, + // dashboardInstance: observable.ref, resetCurrentWidget: action, addDashboard: action, @@ -159,7 +161,7 @@ export default class DashboardStore implements IDashboardSotre { } initDashboard(dashboard: Dashboard) { - this.dashboardInstance = dashboard || new Dashboard() + this.dashboardInstance = dashboard ? new Dashboard().fromJson(dashboard) : new Dashboard() this.selectedWidgets = [] } @@ -208,8 +210,10 @@ export default class DashboardStore implements IDashboardSotre { return dashboardService.saveDashboard(dashboard).then(_dashboard => { runInAction(() => { if (isCreating) { + toast.success('Dashboard created successfully') this.addDashboard(_dashboard) } else { + toast.success('Dashboard updated successfully') this.updateDashboard(_dashboard) } }) @@ -247,10 +251,15 @@ export default class DashboardStore implements IDashboardSotre { deleteDashboard(dashboard: Dashboard): Promise { this.isDeleting = true return dashboardService.deleteDashboard(dashboard.dashboardId).then(() => { + toast.success('Dashboard deleted successfully') runInAction(() => { this.removeDashboard(dashboard) }) - }).finally(() => { + }) + .catch(() => { + toast.error('Dashboard could not be deleted') + }) + .finally(() => { runInAction(() => { this.isDeleting = false }) @@ -364,6 +373,7 @@ export default class DashboardStore implements IDashboardSotre { deleteDashboardWidget(dashboardId: string, widgetId: string) { this.isDeleting = true return dashboardService.deleteWidget(dashboardId, widgetId).then(() => { + toast.success('Widget deleted successfully') runInAction(() => { this.selectedDashboard?.removeWidget(widgetId) }) diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index 1e3746b9e..d322426a7 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -1,6 +1,7 @@ import { makeAutoObservable, runInAction, observable, action, reaction, computed } from "mobx" import Widget, { IWidget } from "./types/widget"; import { metricService } from "App/services"; +import { toast } from 'react-toastify'; export interface IMetricStore { paginatedList: any; @@ -136,8 +137,10 @@ export default class MetricStore implements IMetricStore { return metricService.saveMetric(metric, dashboardId) .then(() => { if (wasCreating) { + toast.success('Metric created successfully') this.addToList(metric) } else { + toast.success('Metric updated successfully') this.updateInList(metric) } }).finally(() => { diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts index 46973f9ff..41410b91c 100644 --- a/frontend/app/mstore/types/dashboard.ts +++ b/frontend/app/mstore/types/dashboard.ts @@ -10,6 +10,7 @@ export interface IDashboard { isValid: boolean isPinned: boolean currentWidget: IWidget + config: any update(data: any): void toJson(): any @@ -31,12 +32,13 @@ export default class Dashboard implements IDashboard { public static get ID_KEY():string { return "dashboardId" } dashboardId: any = undefined name: string = "New Dashboard" - isPublic: boolean = false + isPublic: boolean = true widgets: IWidget[] = [] metrics: any[] = [] isValid: boolean = false isPinned: boolean = false currentWidget: IWidget = new Widget() + config: any = {} constructor() { makeAutoObservable(this, { @@ -75,7 +77,7 @@ export default class Dashboard implements IDashboard { return { dashboardId: this.dashboardId, name: this.name, - isPrivate: this.isPublic, + isPublic: this.isPublic, // widgets: this.widgets.map(w => w.toJson()) // widgets: this.widgets metrics: this.metrics @@ -88,6 +90,7 @@ export default class Dashboard implements IDashboard { this.name = json.name this.isPublic = json.isPublic this.isPinned = json.isPinned + this.config = json.config this.widgets = json.widgets ? json.widgets.map(w => new Widget().fromJson(w)) : [] }) return this diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index 4e6881b58..de7191eb0 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -17,6 +17,7 @@ export interface IWidget { lastModified: Date dashboards: any[] dashboardIds: any[] + config: any position: number data: any @@ -50,6 +51,7 @@ export default class Widget implements IWidget { lastModified: Date = new Date() dashboards: any[] = [] dashboardIds: any[] = [] + config: any = {} position: number = 0 data: any = {} @@ -111,6 +113,8 @@ export default class Widget implements IWidget { this.dashboards = json.dashboards this.owner = json.ownerEmail this.lastModified = DateTime.fromISO(json.editedAt || json.createdAt) + this.config = json.config + this.position = json.config.position }) return this } @@ -122,6 +126,7 @@ export default class Widget implements IWidget { metricOf: this.metricOf, metricValue: this.metricValue, metricType: this.metricType, + viewType: this.viewType, name: this.name, series: this.series.map((series: any) => series.toJson()), } diff --git a/frontend/app/routes.js b/frontend/app/routes.js index 08303f78b..7a6c5379e 100644 --- a/frontend/app/routes.js +++ b/frontend/app/routes.js @@ -104,7 +104,7 @@ export const dashboard = () => '/dashboard'; export const dashboardMetrics = () => '/dashboard/metrics'; export const dashboardSelected = (id = ':dashboardId', hash) => hashed(`/dashboard/${ id }`, hash); -export const dashboardMetricDetails = (id = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ id }/metric/${metricId}`, hash); +export const dashboardMetricDetails = (dashboardId = ':dashboardId', metricId = ':metricId', hash) => hashed(`/dashboard/${ dashboardId }/metric/${metricId}`, hash); export const dashboardMetricCreate = (dashboardId = ':dashboardId', hash) => hashed(`/dashboard/${ dashboardId }/metric/create`, hash); export const metrics = () => `/metrics`; export const metricCreate = () => `/metrics/create`; From c9f7b6e98034b5472a915b62ddf441f9f9c33fb3 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 16:48:26 +0200 Subject: [PATCH 59/87] feat(api): jira integration handle errors --- api/chalicelib/core/integration_jira_cloud.py | 24 ++++++++++++------- api/chalicelib/core/integrations_manager.py | 5 +++- api/chalicelib/utils/jira_client.py | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/api/chalicelib/core/integration_jira_cloud.py b/api/chalicelib/core/integration_jira_cloud.py index ea9c6c24e..7d8c956cf 100644 --- a/api/chalicelib/core/integration_jira_cloud.py +++ b/api/chalicelib/core/integration_jira_cloud.py @@ -15,10 +15,17 @@ class JIRAIntegration(integration_base.BaseIntegration): # TODO: enable super-constructor when OAuth is done # super(JIRAIntegration, self).__init__(jwt, user_id, JIRACloudIntegrationProxy) self._user_id = user_id - i = self.get() - if i is None: + self.integration = self.get() + if self.integration is None: return - self.issue_handler = JIRACloudIntegrationIssue(token=i["token"], username=i["username"], url=i["url"]) + self.integration["valid"] = True + try: + self.issue_handler = JIRACloudIntegrationIssue(token=self.integration["token"], + username=self.integration["username"], + url=self.integration["url"]) + except Exception as e: + self.issue_handler = None + self.integration["valid"] = False @property def provider(self): @@ -37,10 +44,10 @@ class JIRAIntegration(integration_base.BaseIntegration): return helper.dict_to_camel_case(cur.fetchone()) def get_obfuscated(self): - integration = self.get() - if integration is None: + if self.integration is None: return None - integration["token"] = obfuscate_string(integration["token"]) + integration = dict(self.integration) + integration["token"] = obfuscate_string(self.integration["token"]) integration["provider"] = self.provider.lower() return integration @@ -90,14 +97,13 @@ class JIRAIntegration(integration_base.BaseIntegration): return {"state": "success"} def add_edit(self, data): - s = self.get() - if s is not None: + if self.integration is not None: return self.update( changes={ "username": data["username"], "token": data["token"] \ if data.get("token") and len(data["token"]) > 0 and data["token"].find("***") == -1 \ - else s["token"], + else self.integration["token"], "url": data["url"] }, obfuscate=True diff --git a/api/chalicelib/core/integrations_manager.py b/api/chalicelib/core/integrations_manager.py index fca271870..ef63a7d96 100644 --- a/api/chalicelib/core/integrations_manager.py +++ b/api/chalicelib/core/integrations_manager.py @@ -36,7 +36,10 @@ def get_integration(tenant_id, user_id, tool=None): if tool not in SUPPORTED_TOOLS: return {"errors": [f"issue tracking tool not supported yet, available: {SUPPORTED_TOOLS}"]}, None if tool == integration_jira_cloud.PROVIDER: - return None, integration_jira_cloud.JIRAIntegration(tenant_id=tenant_id, user_id=user_id) + integration = integration_jira_cloud.JIRAIntegration(tenant_id=tenant_id, user_id=user_id) + if integration.integration is not None and not integration.integration.get("valid", True): + return {"errors": ["JIRA: connexion issue/unauthorized"]}, integration + return None, integration elif tool == integration_github.PROVIDER: return None, integration_github.GitHubIntegration(tenant_id=tenant_id, user_id=user_id) return {"errors": ["lost integration"]}, None diff --git a/api/chalicelib/utils/jira_client.py b/api/chalicelib/utils/jira_client.py index 40bc0d66c..b1734660c 100644 --- a/api/chalicelib/utils/jira_client.py +++ b/api/chalicelib/utils/jira_client.py @@ -22,7 +22,7 @@ class JiraManager: except Exception as e: print("!!! JIRA AUTH ERROR") print(e) - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: Authentication error") + raise e def set_jira_project_id(self, project_id): self._config["JIRA_PROJECT_ID"] = project_id From ad706ededd38e455bc6e9245aa509ed3fc212b12 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 6 Apr 2022 16:49:26 +0200 Subject: [PATCH 60/87] feat(ui) - dashboard - wip --- .../DashboardSelectionModal.tsx | 73 +++++++++++++++++++ .../DashboardSelectionModal/index.ts | 0 .../components/MetricsList/MetricsList.tsx | 2 +- .../components/WidgetForm/WidgetForm.tsx | 23 ++++-- .../components/WidgetName/WidgetName.tsx | 5 +- .../components/WidgetView/WidgetView.tsx | 18 +++-- .../shared/DropdownPlain/DropdownPlain.css | 1 + .../shared/DropdownPlain/DropdownPlain.tsx | 2 +- frontend/app/mstore/dashboardStore.ts | 32 ++++---- frontend/app/mstore/metricStore.ts | 14 ++-- frontend/app/mstore/types/dashboard.ts | 11 +++ frontend/app/mstore/types/widget.ts | 2 +- frontend/app/services/DashboardService.ts | 15 ++++ 13 files changed, 162 insertions(+), 36 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx create mode 100644 frontend/app/components/Dashboard/components/DashboardSelectionModal/index.ts diff --git a/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx b/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx new file mode 100644 index 000000000..f7a2c46ac --- /dev/null +++ b/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx @@ -0,0 +1,73 @@ +import { useObserver } from 'mobx-react-lite'; +import React from 'react'; +import { Button, Modal, Form, Icon } from 'UI'; +import { useStore } from 'App/mstore' +import DropdownPlain from 'Shared/DropdownPlain'; + +interface Props { + metricId: string, + show: boolean; + closeHandler?: () => void; +} +function DashboardSelectionModal(props: Props) { + const { show, metricId, closeHandler } = props; + const { dashboardStore } = useStore(); + const dashboardOptions = dashboardStore.dashboards.map((i: any) => ({ + key: i.id, + text: i.name, + value: i.dashboardId, + })); + const [selectedId, setSelectedId] = React.useState(dashboardOptions[0].value); + + const onSave = () => { + const dashboard = dashboardStore.getDashboard(selectedId) + if (dashboard) { + dashboardStore.addWidgetToDashboard(dashboard, [metricId]).then(closeHandler) + } + } + + return useObserver(() => ( + + +
{ 'Add to selected Dashboard' }
+ +
+ + +
+
+ + + setSelectedId(value)} + /> + +
+
+
+ +
+ + +
+
+
+ )); +} + +export default DashboardSelectionModal; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardSelectionModal/index.ts b/frontend/app/components/Dashboard/components/DashboardSelectionModal/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index dd1af190b..9bc01dd01 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -13,7 +13,7 @@ function MetricsList(props: Props) { const metricsSearch = useObserver(() => metricStore.metricsSearch); const filterRE = getRE(metricsSearch, 'i'); - const list = metrics.filter(w => filterRE.test(w.name)); + const list = useObserver(() => metrics.filter(w => filterRE.test(w.name))); return useObserver(() => ( diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index abe92223a..058be4d1c 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import DropdownPlain from 'Shared/DropdownPlain'; import { metricTypes, metricOf, issueOptions } from 'App/constants/filterOptions'; import { FilterKey } from 'Types/filter/filterType'; @@ -9,6 +9,7 @@ import FilterSeries from '../FilterSeries'; import { withRouter } from 'react-router-dom'; import { confirm } from 'UI/Confirmation'; import { withSiteId, dashboardMetricDetails, metricDetails } from 'App/routes' +import DashboardSelectionModal from '../DashboardSelectionModal/DashboardSelectionModal'; interface Props { history: any; @@ -17,8 +18,10 @@ interface Props { } function WidgetForm(props: Props) { + const [showDashboardSelectionModal, setShowDashboardSelectionModal] = useState(false); const { history, match: { params: { siteId, dashboardId, metricId } } } = props; const { metricStore } = useStore(); + const isSaving = useObserver(() => metricStore.isSaving); const metric: any = useObserver(() => metricStore.instance); const timeseriesOptions = metricOf.filter(i => i.type === 'timeseries'); @@ -52,12 +55,14 @@ function WidgetForm(props: Props) { const onSave = () => { const wasCreating = !metric.exists() - metricStore.save(metric, dashboardId).then(() => { + metricStore.save(metric, dashboardId).then((metric) => { if (wasCreating) { if (parseInt(dashboardId) > 0) { - history.push(withSiteId(dashboardMetricDetails(parseInt(dashboardId)), siteId)); + history.push(withSiteId(dashboardMetricDetails(parseInt(dashboardId), metric.metricId), siteId)); + history.go(0) } else { history.push(withSiteId(metricDetails(metric.metricId), siteId)); + history.go(0) } } @@ -179,8 +184,9 @@ function WidgetForm(props: Props) { primary size="small" onClick={onSave} + disabled={isSaving} > - Save + {metric.exists() ? 'Update' : 'Create'}
{metric.exists() && ( @@ -189,7 +195,7 @@ function WidgetForm(props: Props) { Delete - @@ -197,6 +203,13 @@ function WidgetForm(props: Props) { )}
+ + + setShowDashboardSelectionModal(false)} + />
)); } diff --git a/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx b/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx index 0cbfafd49..09e3b66b3 100644 --- a/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx +++ b/frontend/app/components/Dashboard/components/WidgetName/WidgetName.tsx @@ -5,9 +5,10 @@ interface Props { name: string; onUpdate: (name) => void; seriesIndex?: number; + canEdit?: boolean } function WidgetName(props: Props) { - const { seriesIndex = 1 } = props; + const { canEdit = true } = props; const [editing, setEditing] = useState(false) const [name, setName] = useState(props.name) const ref = useRef(null) @@ -49,7 +50,7 @@ function WidgetName(props: Props) {
{ name }
)} -
setEditing(true)}>
+ { canEdit &&
setEditing(true)}>
}
); } diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index f86e22302..82442bbcc 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { withRouter } from 'react-router-dom'; import { useStore } from 'App/mstore'; import WidgetForm from '../WidgetForm'; @@ -15,16 +15,18 @@ interface Props { } function WidgetView(props: Props) { const { match: { params: { siteId, dashboardId, metricId } } } = props; - const [expanded, setExpanded] = useState(true); const { metricStore } = useStore(); const widget = useObserver(() => metricStore.instance); const loading = useObserver(() => metricStore.isLoading); + const [expanded, setExpanded] = useState(false); + + useEffect(() => { + setExpanded(!widget.exists()) + }, [widget]) React.useEffect(() => { if (metricId && metricId !== 'create') { - metricStore.fetch(metricId).then((metric) => { - // metricStore.init(metric) - }); + metricStore.fetch(metricId); } else { metricStore.init(); } @@ -45,7 +47,11 @@ function WidgetView(props: Props) {

- metricStore.merge({ name })} /> + metricStore.merge({ name })} + canEdit={expanded} + />

void; icon?: string; direction?: string; - value: any; + value?: any; multiple?: boolean; } diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 3b497cfa6..85832699e 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -41,10 +41,8 @@ export interface IDashboardSotre { // initDashboard(dashboard: IDashboard): void addDashboard(dashboard: IDashboard): void removeDashboard(dashboard: IDashboard): void - getDashboard(dashboardId: string): void - getDashboardIndex(dashboardId: string): number + getDashboard(dashboardId: string): IDashboard|null getDashboardCount(): void - getDashboardIndexByDashboardId(dashboardId: string): number updateDashboard(dashboard: IDashboard): void selectDashboardById(dashboardId: string): void setSiteId(siteId: any): void @@ -53,6 +51,7 @@ export interface IDashboardSotre { saveMetric(metric: IWidget, dashboardId?: string): Promise fetchTemplates(): Promise deleteDashboardWidget(dashboardId: string, widgetId: string): Promise + addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise } export default class DashboardStore implements IDashboardSotre { siteId: any = null @@ -86,10 +85,8 @@ export default class DashboardStore implements IDashboardSotre { removeDashboard: action, updateDashboard: action, getDashboard: action, - getDashboardIndex: action, getDashboardByIndex: action, getDashboardCount: action, - getDashboardIndexByDashboardId: action, selectDashboardById: action, selectDefaultDashboard: action, toJson: action, @@ -287,12 +284,8 @@ export default class DashboardStore implements IDashboardSotre { this.dashboards = this.dashboards.filter(d => d.dashboardId !== dashboard.dashboardId) } - getDashboard(dashboardId: string) { - return this.dashboards.find(d => d.dashboardId === dashboardId) - } - - getDashboardIndex(dashboardId: string) { - return this.dashboards.findIndex(d => d.dashboardId === dashboardId) + getDashboard(dashboardId: string): IDashboard|null { + return this.dashboards.find(d => d.dashboardId === dashboardId) || null } getDashboardByIndex(index: number) { @@ -303,10 +296,6 @@ export default class DashboardStore implements IDashboardSotre { return this.dashboards.length } - getDashboardIndexByDashboardId(dashboardId: string) { - return this.dashboards.findIndex(d => d.dashboardId === dashboardId) - } - updateDashboard(dashboard: Dashboard) { const index = this.dashboards.findIndex(d => d.dashboardId === dashboard.dashboardId) if (index >= 0) { @@ -381,6 +370,19 @@ export default class DashboardStore implements IDashboardSotre { this.isDeleting = false }) } + + addWidgetToDashboard(dashboard: IDashboard, metricIds: any) : Promise { + this.isSaving = true + return dashboardService.addWidget(dashboard, metricIds) + .then(response => { + toast.success('Widget added successfully') + }).catch(() => { + toast.error('Widget could not be added') + }).finally(() => { + this.isSaving = false + }) + + } } function getRandomWidget() { diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index d322426a7..4521c4fff 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -90,7 +90,7 @@ export default class MetricStore implements IMetricStore { } updateKey(key: string, value: any) { - this.instance[key] = value + this[key] = value } merge(object: any) { @@ -131,18 +131,22 @@ export default class MetricStore implements IMetricStore { } // API Communication - save(metric: IWidget, dashboardId?: string) { + save(metric: IWidget, dashboardId?: string): Promise { const wasCreating = !metric[Widget.ID_KEY] this.isSaving = true return metricService.saveMetric(metric, dashboardId) - .then(() => { + .then((metric) => { + const _metric = new Widget().fromJson(metric) if (wasCreating) { toast.success('Metric created successfully') - this.addToList(metric) + this.addToList(_metric) } else { toast.success('Metric updated successfully') - this.updateInList(metric) + this.updateInList(_metric) } + return _metric + }).catch(() => { + toast.error('Error saving metric') }).finally(() => { this.isSaving = false }) diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts index 41410b91c..bd6551ba3 100644 --- a/frontend/app/mstore/types/dashboard.ts +++ b/frontend/app/mstore/types/dashboard.ts @@ -27,6 +27,7 @@ export interface IDashboard { swapWidgetPosition(positionA: number, positionB: number): void sortWidgets(): void exists(): boolean + toggleMetrics(metricId: string): void } export default class Dashboard implements IDashboard { public static get ID_KEY():string { return "dashboardId" } @@ -46,6 +47,7 @@ export default class Dashboard implements IDashboard { isPublic: observable, widgets: observable, isValid: observable, + metrics: observable, toJson: action, fromJson: action, @@ -61,6 +63,7 @@ export default class Dashboard implements IDashboard { sortWidgets: action, swapWidgetPosition: action, update: action, + toggleMetrics: action }) this.validate(); @@ -161,4 +164,12 @@ export default class Dashboard implements IDashboard { exists() { return this.dashboardId !== undefined } + + toggleMetrics(metricId: string) { + if (this.metrics.includes(metricId)) { + this.metrics = this.metrics.filter(m => m !== metricId) + } else { + this.metrics.push(metricId) + } + } } \ No newline at end of file diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index de7191eb0..7749600e5 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -112,7 +112,7 @@ export default class Widget implements IWidget { this.series = json.series ? json.series.map((series: any) => new FilterSeries().fromJson(series)) : [], this.dashboards = json.dashboards this.owner = json.ownerEmail - this.lastModified = DateTime.fromISO(json.editedAt || json.createdAt) + this.lastModified = DateTime.fromMillis(json.editedAt || json.createdAt) this.config = json.config this.position = json.config.position }) diff --git a/frontend/app/services/DashboardService.ts b/frontend/app/services/DashboardService.ts index 3208fd941..3bb99b9b4 100644 --- a/frontend/app/services/DashboardService.ts +++ b/frontend/app/services/DashboardService.ts @@ -14,6 +14,7 @@ export interface IDashboardService { saveMetric(metric: IWidget, dashboardId?: string): Promise + addWidget(dashboard: IDashboard, metricIds: []): Promise saveWidget(dashboardId: string, widget: IWidget): Promise deleteWidget(dashboardId: string, widgetId: string): Promise } @@ -81,6 +82,20 @@ export default class DashboardService implements IDashboardService { } } + /** + * Add a widget to a dashboard. + * @param dashboard + * @param metricIds + * @returns + */ + addWidget(dashboard: IDashboard, metricIds: any): Promise { + const data = dashboard.toJson() + data.metrics = metricIds + return this.client.put(`/dashboards/${dashboard.dashboardId}`, data) + .then(response => response.json()) + .then(response => response.data || {}); + } + /** * Delete a dashboard. * @param dashboardId From 7ebcbb8b0b1aeda348aafe1323152a4cfd2f8a3c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 16:53:27 +0200 Subject: [PATCH 61/87] feat(utilities): EE-WS send SESSION_RECONNECTED on session's reconnection --- ee/utilities/servers/websocket-cluster.js | 2 ++ ee/utilities/servers/websocket.js | 2 ++ utilities/servers/websocket.js | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ee/utilities/servers/websocket-cluster.js b/ee/utilities/servers/websocket-cluster.js index c044043a5..998a457df 100644 --- a/ee/utilities/servers/websocket-cluster.js +++ b/ee/utilities/servers/websocket-cluster.js @@ -14,6 +14,7 @@ const AGENT_DISCONNECT = "AGENT_DISCONNECTED"; const AGENTS_CONNECTED = "AGENTS_CONNECTED"; const NO_SESSIONS = "SESSION_DISCONNECTED"; const SESSION_ALREADY_CONNECTED = "SESSION_ALREADY_CONNECTED"; +const SESSION_RECONNECTED = "SESSION_RECONNECTED"; const REDIS_URL = process.env.REDIS_URL || "redis://localhost:6379"; const pubClient = createClient({url: REDIS_URL}); const subClient = pubClient.duplicate(); @@ -309,6 +310,7 @@ module.exports = { debug && console.log(`notifying new session about agent-existence`); let agents_ids = await get_all_agents_ids(io, socket); io.to(socket.id).emit(AGENTS_CONNECTED, agents_ids); + socket.to(socket.peerId).emit(SESSION_RECONNECTED, socket.id); } } else if (c_sessions <= 0) { diff --git a/ee/utilities/servers/websocket.js b/ee/utilities/servers/websocket.js index 0bd397d96..256286351 100644 --- a/ee/utilities/servers/websocket.js +++ b/ee/utilities/servers/websocket.js @@ -12,6 +12,7 @@ const AGENT_DISCONNECT = "AGENT_DISCONNECTED"; const AGENTS_CONNECTED = "AGENTS_CONNECTED"; const NO_SESSIONS = "SESSION_DISCONNECTED"; const SESSION_ALREADY_CONNECTED = "SESSION_ALREADY_CONNECTED"; +const SESSION_RECONNECTED = "SESSION_RECONNECTED"; let io; const debug = process.env.debug === "1" || false; @@ -287,6 +288,7 @@ module.exports = { debug && console.log(`notifying new session about agent-existence`); let agents_ids = await get_all_agents_ids(io, socket); io.to(socket.id).emit(AGENTS_CONNECTED, agents_ids); + socket.to(socket.peerId).emit(SESSION_RECONNECTED, socket.id); } } else if (c_sessions <= 0) { diff --git a/utilities/servers/websocket.js b/utilities/servers/websocket.js index 3cd22f3fc..7371f206c 100644 --- a/utilities/servers/websocket.js +++ b/utilities/servers/websocket.js @@ -12,7 +12,7 @@ const AGENT_DISCONNECT = "AGENT_DISCONNECTED"; const AGENTS_CONNECTED = "AGENTS_CONNECTED"; const NO_SESSIONS = "SESSION_DISCONNECTED"; const SESSION_ALREADY_CONNECTED = "SESSION_ALREADY_CONNECTED"; -const SESSION_RECONNECTED = "SESSION_RECONNECTED" +const SESSION_RECONNECTED = "SESSION_RECONNECTED"; let io; const debug = process.env.debug === "1" || false; From 7e3597521c19b39bb367d7c13dc959a140ab8a2c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 17:14:26 +0200 Subject: [PATCH 62/87] feat(db): dashboard schema for init file feat(db): dashboard schema for EE init file feat(db): dashboard schema for EE delta file --- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 80 +++++++++++++++++++ .../db/init_dbs/postgresql/init_schema.sql | 80 +++++++++++++++++-- .../db/init_dbs/postgresql/init_schema.sql | 27 ++++++- 3 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql new file mode 100644 index 000000000..15d5f314b --- /dev/null +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -0,0 +1,80 @@ +BEGIN; +CREATE OR REPLACE FUNCTION openreplay_version() + RETURNS text AS +$$ +SELECT 'v1.5.5-ee' +$$ LANGUAGE sql IMMUTABLE; + + +CREATE TABLE IF NOT EXISTS dashboards +( + dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + name text NOT NULL, + is_public boolean NOT NULL DEFAULT TRUE, + is_pinned boolean NOT NULL DEFAULT FALSE, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + deleted_at timestamp NULL DEFAULT NULL +); + + +ALTER TABLE IF EXISTS metrics + DROP CONSTRAINT IF EXISTS null_project_id_for_template_only, + DROP CONSTRAINT IF EXISTS unique_key; + +ALTER TABLE IF EXISTS metrics + ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', + ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS key text NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, + ALTER COLUMN project_id DROP NOT NULL, + ADD CONSTRAINT null_project_id_for_template_only + CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), + ADD CONSTRAINT unique_key UNIQUE (key); + + + +CREATE TABLE IF NOT EXISTS dashboard_widgets +( + widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, + metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + config jsonb NOT NULL DEFAULT '{}'::jsonb +); + + +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu') +ON CONFLICT (key) DO UPDATE SET name=excluded.name, + category=excluded.category, + config=excluded.config, + is_predefined=excluded.is_predefined, + is_template=excluded.is_template, + is_public=excluded.is_public; + +COMMIT; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; \ No newline at end of file diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 9adab50e0..506853ffc 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -7,7 +7,7 @@ CREATE EXTENSION IF NOT EXISTS pgcrypto; CREATE OR REPLACE FUNCTION openreplay_version() RETURNS text AS $$ -SELECT 'v1.5.4-ee' +SELECT 'v1.5.5-ee' $$ LANGUAGE sql IMMUTABLE; @@ -106,6 +106,8 @@ $$ ('assigned_sessions'), ('autocomplete'), ('basic_authentication'), + ('dashboards'), + ('dashboard_widgets'), ('errors'), ('funnels'), ('integrations'), @@ -787,22 +789,32 @@ $$ CREATE INDEX IF NOT EXISTS traces_tenant_id_idx ON traces (tenant_id); CREATE TYPE metric_type AS ENUM ('timeseries','table'); - CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart'); + CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart'); CREATE TABLE IF NOT EXISTS metrics ( metric_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + project_id integer NULL REFERENCES projects (project_id) ON DELETE CASCADE, user_id integer REFERENCES users (user_id) ON DELETE SET NULL, name text NOT NULL, is_public boolean NOT NULL DEFAULT FALSE, active boolean NOT NULL DEFAULT TRUE, - created_at timestamp DEFAULT timezone('utc'::text, now()) not null, + created_at timestamp default timezone('utc'::text, now()) not null, deleted_at timestamp, + edited_at timestamp, metric_type metric_type NOT NULL DEFAULT 'timeseries', view_type metric_view_type NOT NULL DEFAULT 'lineChart', metric_of text NOT NULL DEFAULT 'sessionCount', metric_value text[] NOT NULL DEFAULT '{}'::text[], - metric_format text + metric_format text, + category text NULL DEFAULT 'custom', + is_pinned boolean NOT NULL DEFAULT FALSE, + is_predefined boolean NOT NULL DEFAULT FALSE, + is_template boolean NOT NULL DEFAULT FALSE, + key text NULL DEFAULT NULL, + config jsonb NOT NULL DEFAULT '{}'::jsonb, + CONSTRAINT null_project_id_for_template_only + CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), + CONSTRAINT unique_key UNIQUE (key) ); CREATE INDEX IF NOT EXISTS metrics_user_id_is_public_idx ON public.metrics (user_id, is_public); CREATE TABLE IF NOT EXISTS metric_series @@ -817,6 +829,29 @@ $$ ); CREATE INDEX IF NOT EXISTS metric_series_metric_id_idx ON public.metric_series (metric_id); + + CREATE TABLE dashboards + ( + dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + name text NOT NULL, + is_public boolean NOT NULL DEFAULT TRUE, + is_pinned boolean NOT NULL DEFAULT FALSE, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + deleted_at timestamp NULL DEFAULT NULL + ); + + CREATE TABLE dashboard_widgets + ( + widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, + metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + config jsonb NOT NULL DEFAULT '{}'::jsonb + ); + CREATE TABLE IF NOT EXISTS searches ( search_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, @@ -948,10 +983,13 @@ $$ CREATE INDEX IF NOT EXISTS pages_session_id_timestamp_loadgt0NN_idx ON events.pages (session_id, timestamp) WHERE load_time > 0 AND load_time IS NOT NULL; CREATE INDEX IF NOT EXISTS pages_session_id_timestamp_visualgt0nn_idx ON events.pages (session_id, timestamp) WHERE visually_complete > 0 AND visually_complete IS NOT NULL; CREATE INDEX IF NOT EXISTS pages_timestamp_metgt0_idx ON events.pages (timestamp) WHERE response_time > 0 OR - first_paint_time > 0 OR - dom_content_loaded_time > 0 OR + first_paint_time > + 0 OR + dom_content_loaded_time > + 0 OR ttfb > 0 OR - time_to_interactive > 0; + time_to_interactive > + 0; CREATE INDEX IF NOT EXISTS pages_session_id_speed_indexgt0nn_idx ON events.pages (session_id, speed_index) WHERE speed_index > 0 AND speed_index IS NOT NULL; CREATE INDEX IF NOT EXISTS pages_session_id_timestamp_dom_building_timegt0nn_idx ON events.pages (session_id, timestamp, dom_building_time) WHERE dom_building_time > 0 AND dom_building_time IS NOT NULL; CREATE INDEX IF NOT EXISTS pages_base_path_session_id_timestamp_idx ON events.pages (base_path, session_id, timestamp); @@ -1219,5 +1257,31 @@ $$ $$ LANGUAGE plpgsql; +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu') +ON CONFLICT (key) DO UPDATE SET name=excluded.name, + category=excluded.category, + config=excluded.config, + is_predefined=excluded.is_predefined, + is_template=excluded.is_template, + is_public=excluded.is_public; COMMIT; \ No newline at end of file diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 5114d2433..8a0d2e807 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -6,7 +6,7 @@ CREATE SCHEMA IF NOT EXISTS events; CREATE OR REPLACE FUNCTION openreplay_version() RETURNS text AS $$ -SELECT 'v1.5.4' +SELECT 'v1.5.5' $$ LANGUAGE sql IMMUTABLE; -- --- accounts.sql --- @@ -960,7 +960,8 @@ $$ key text NULL DEFAULT NULL, config jsonb NOT NULL DEFAULT '{}'::jsonb, CONSTRAINT null_project_id_for_template_only - CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ) + CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), + CONSTRAINT unique_key UNIQUE (key) ); CREATE INDEX metrics_user_id_is_public_idx ON public.metrics (user_id, is_public); @@ -976,6 +977,28 @@ $$ ); CREATE INDEX metric_series_metric_id_idx ON public.metric_series (metric_id); + CREATE TABLE dashboards + ( + dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + name text NOT NULL, + is_public boolean NOT NULL DEFAULT TRUE, + is_pinned boolean NOT NULL DEFAULT FALSE, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + deleted_at timestamp NULL DEFAULT NULL + ); + + CREATE TABLE dashboard_widgets + ( + widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, + metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, + user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, + created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), + config jsonb NOT NULL DEFAULT '{}'::jsonb + ); + CREATE TABLE searches ( From 54ef9a248b15da4d7fa9b59cc5e6bfd89834fcf4 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 17:51:07 +0200 Subject: [PATCH 63/87] feat(api): dashboard format created_at and edited_at --- api/chalicelib/core/dashboards2.py | 8 ++++++++ api/routers/core.py | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index afe65d71a..ac382f3d7 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -4,6 +4,7 @@ import schemas from chalicelib.core import custom_metrics, dashboard from chalicelib.utils import helper from chalicelib.utils import pg_client +from chalicelib.utils.TimeUTC import TimeUTC CATEGORY_DESCRIPTION = { 'categ1': 'lorem', @@ -21,6 +22,9 @@ def get_templates(project_id, user_id): rows = cur.fetchall() for r in rows: r["description"] = CATEGORY_DESCRIPTION.get(r["category"], "") + for w in r["widgets"]: + w["created_at"] = TimeUTC.datetime_to_timestamp(w["created_at"]) + w["edited_at"] = TimeUTC.datetime_to_timestamp(w["edited_at"]) return helper.list_to_camel_case(rows) @@ -85,6 +89,10 @@ def get_dashboard(project_id, user_id, dashboard_id): params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id} cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + if row is not None: + for w in row["widgets"]: + row["created_at"] = TimeUTC.datetime_to_timestamp(w["created_at"]) + row["edited_at"] = TimeUTC.datetime_to_timestamp(w["edited_at"]) return helper.dict_to_camel_case(row) diff --git a/api/routers/core.py b/api/routers/core.py index 20864288d..06743c054 100644 --- a/api/routers/core.py +++ b/api/routers/core.py @@ -394,7 +394,7 @@ def delete_sumologic(projectId: int, context: schemas.CurrentContext = Depends(O def get_integration_status(context: schemas.CurrentContext = Depends(OR_context)): error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id, user_id=context.user_id) - if error is not None: + if error is not None and integration is None: return {"data": {}} return {"data": integration.get_obfuscated()} @@ -406,7 +406,7 @@ def add_edit_jira_cloud(data: schemas.JiraGithubSchema = Body(...), error, integration = integrations_manager.get_integration(tool=integration_jira_cloud.PROVIDER, tenant_id=context.tenant_id, user_id=context.user_id) - if error is not None: + if error is not None and integration is None: return error data.provider = integration_jira_cloud.PROVIDER return {"data": integration.add_edit(data=data.dict())} @@ -429,7 +429,7 @@ def add_edit_github(data: schemas.JiraGithubSchema = Body(...), def delete_default_issue_tracking_tool(context: schemas.CurrentContext = Depends(OR_context)): error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id, user_id=context.user_id) - if error is not None: + if error is not None and integration is None: return error return {"data": integration.delete()} From 4602de3caf70f3feebbd2cf1924ed2fca0a1183c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 7 Apr 2022 11:29:40 +0200 Subject: [PATCH 64/87] feat(ui) - dashboard - wip --- .../CustomMetricPieChart.tsx | 157 +++++++----------- .../components/MetricsList/MetricsList.tsx | 17 +- .../MetricsSearch/MetricsSearch.tsx | 19 ++- .../components/WidgetForm/WidgetForm.tsx | 2 - .../components/WidgetView/WidgetView.tsx | 6 +- frontend/app/mstore/metricStore.ts | 19 ++- frontend/app/mstore/types/widget.ts | 3 +- 7 files changed, 102 insertions(+), 121 deletions(-) diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx index e48dd20dd..91dbed7b5 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx @@ -35,8 +35,7 @@ function CustomMetricPieChart(props: Props) { } } return ( -
- + { - const RADIAN = Math.PI / 180; - let radius1 = 15 + innerRadius + (outerRadius - innerRadius); - let radius2 = innerRadius + (outerRadius - innerRadius); - let x2 = cx + radius1 * Math.cos(-midAngle * RADIAN); - let y2 = cy + radius1 * Math.sin(-midAngle * RADIAN); - let x1 = cx + radius2 * Math.cos(-midAngle * RADIAN); - let y1 = cy + radius2 * Math.sin(-midAngle * RADIAN); + cx, + cy, + midAngle, + innerRadius, + outerRadius, + value, + index + }) => { + const RADIAN = Math.PI / 180; + let radius1 = 15 + innerRadius + (outerRadius - innerRadius); + let radius2 = innerRadius + (outerRadius - innerRadius); + let x2 = cx + radius1 * Math.cos(-midAngle * RADIAN); + let y2 = cy + radius1 * Math.sin(-midAngle * RADIAN); + let x1 = cx + radius2 * Math.cos(-midAngle * RADIAN); + let y1 = cy + radius2 * Math.sin(-midAngle * RADIAN); - const percentage = value * 100 / data.values.reduce((a, b) => a + b.sessionCount, 0); - - if (percentage<3){ - return null; - } - - return( - - ) - }} - label={({ - cx, - cy, - midAngle, - innerRadius, - outerRadius, - value, - index - }) => { - const RADIAN = Math.PI / 180; - let radius = 20 + innerRadius + (outerRadius - innerRadius); - let x = cx + radius * Math.cos(-midAngle * RADIAN); - let y = cy + radius * Math.sin(-midAngle * RADIAN); - const percentage = (value / data.values.reduce((a, b) => a + b.sessionCount, 0)) * 100; - let name = data.values[index].name || 'Unidentified'; - name = name.length > 20 ? name.substring(0, 20) + '...' : name; - if (percentage<3){ - return null; - } - return ( - cx ? "start" : "end"} - dominantBaseline="central" - fill='#666' - > - {name || 'Unidentified'} {numberWithCommas(value)} - - ); - }} - // label={({ - // cx, - // cy, - // midAngle, - // innerRadius, - // outerRadius, - // value, - // index - // }) => { - // const RADIAN = Math.PI / 180; - // const radius = 30 + innerRadius + (outerRadius - innerRadius); - // const x = cx + radius * Math.cos(-midAngle * RADIAN); - // const y = cy + radius * Math.sin(-midAngle * RADIAN); - - // return ( - // cx ? "start" : "end"} - // dominantBaseline="top" - // fontSize={10} - // > - // {data.values[index].name} ({value}) - // - // ); - // }} + const percentage = value * 100 / data.values.reduce((a, b) => a + b.sessionCount, 0); + + if (percentage<3){ + return null; + } + + return( + + ) + }} + label={({ + cx, + cy, + midAngle, + innerRadius, + outerRadius, + value, + index + }) => { + const RADIAN = Math.PI / 180; + let radius = 20 + innerRadius + (outerRadius - innerRadius); + let x = cx + radius * Math.cos(-midAngle * RADIAN); + let y = cy + radius * Math.sin(-midAngle * RADIAN); + const percentage = (value / data.values.reduce((a, b) => a + b.sessionCount, 0)) * 100; + let name = data.values[index].name || 'Unidentified'; + name = name.length > 20 ? name.substring(0, 20) + '...' : name; + if (percentage<3){ + return null; + } + return ( + cx ? "start" : "end"} + dominantBaseline="central" + fill='#666' + > + {name || 'Unidentified'} {numberWithCommas(value)} + + ); + }} > - {data.values.map((entry, index) => ( + {data && data.values && data.values.map((entry, index) => ( - ))} + ))} - +
Top 5
-
-
+ ) } diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index 9bc01dd01..349386980 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -4,17 +4,22 @@ import { NoContent, Pagination } from 'UI'; import { useStore } from 'App/mstore'; import { getRE } from 'App/utils'; import MetricListItem from '../MetricListItem'; +import { sliceListPerPage } from 'App/utils'; interface Props { } function MetricsList(props: Props) { const { metricStore } = useStore(); const metrics = useObserver(() => metricStore.metrics); - const lenth = metrics.length; - const metricsSearch = useObserver(() => metricStore.metricsSearch); - const filterRE = getRE(metricsSearch, 'i'); - const list = useObserver(() => metrics.filter(w => filterRE.test(w.name))); - + const filterList = (list) => { + const filterRE = getRE(metricsSearch, 'i'); + let _list = list.filter(w => { + return filterRE.test(w.name) || filterRE.test(w.metricType) || filterRE.test(w.owner) ; + }); + return _list + } + const list: any = metricsSearch !== '' ? filterList(metrics) : metrics; + const lenth = list.length; return useObserver(() => ( @@ -28,7 +33,7 @@ function MetricsList(props: Props) {
Last Modified
- {list.map((metric: any) => ( + {sliceListPerPage(list, metricStore.page - 1, metricStore.pageSize).map((metric: any) => ( ))}
diff --git a/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx index ce4bdbe24..6941b6ec0 100644 --- a/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx +++ b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx @@ -1,22 +1,31 @@ import { useObserver } from 'mobx-react-lite'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useStore } from 'App/mstore'; import { Icon } from 'UI'; +import { debounce } from 'App/utils'; +let debounceUpdate: any = () => {} function MetricsSearch(props) { - const { dashboardStore } = useStore(); - const metricsSearch = useObserver(() => dashboardStore.metricsSearch); + const { metricStore } = useStore(); + const [query, setQuery] = useState(metricStore.metricsSearch); + useEffect(() => { + debounceUpdate = debounce((key, value) => metricStore.updateKey(key, value), 500); + }, []) + const write = ({ target: { name, value } }) => { + setQuery(value); + debounceUpdate('metricsSearch', value); + } return useObserver(() => (
dashboardStore.updateKey(name, value)} + onChange={write} />
)); diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 058be4d1c..bc7803c85 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -59,10 +59,8 @@ function WidgetForm(props: Props) { if (wasCreating) { if (parseInt(dashboardId) > 0) { history.push(withSiteId(dashboardMetricDetails(parseInt(dashboardId), metric.metricId), siteId)); - history.go(0) } else { history.push(withSiteId(metricDetails(metric.metricId), siteId)); - history.go(0) } } diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index 82442bbcc..8a5391948 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -18,11 +18,7 @@ function WidgetView(props: Props) { const { metricStore } = useStore(); const widget = useObserver(() => metricStore.instance); const loading = useObserver(() => metricStore.isLoading); - const [expanded, setExpanded] = useState(false); - - useEffect(() => { - setExpanded(!widget.exists()) - }, [widget]) + const [expanded, setExpanded] = useState(!metricId || metricId === 'create'); React.useEffect(() => { if (metricId && metricId !== 'create') { diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index 4521c4fff..455452adb 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -43,7 +43,7 @@ export default class MetricStore implements IMetricStore { instance: IWidget = new Widget() page: number = 1 - pageSize: number = 10 + pageSize: number = 4 metricsSearch: string = "" sort: any = {} @@ -74,14 +74,14 @@ export default class MetricStore implements IMetricStore { paginatedList: computed, }) - // reaction( - // () => this.metricsSearch, - // (metricsSearch) => { // TODO filter the list for View - // console.log('metricsSearch', metricsSearch) - // this.page = 1 - // this.paginatedList() - // } - // ) + reaction( + () => this.metricsSearch, + (metricsSearch) => { // TODO filter the list for View + console.log('metricsSearch', metricsSearch) + this.page = 1 + this.paginatedList + } + ) } // State Actions @@ -140,6 +140,7 @@ export default class MetricStore implements IMetricStore { if (wasCreating) { toast.success('Metric created successfully') this.addToList(_metric) + this.instance = _metric } else { toast.success('Metric updated successfully') this.updateInList(_metric) diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index 7749600e5..672c84659 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -112,7 +112,8 @@ export default class Widget implements IWidget { this.series = json.series ? json.series.map((series: any) => new FilterSeries().fromJson(series)) : [], this.dashboards = json.dashboards this.owner = json.ownerEmail - this.lastModified = DateTime.fromMillis(json.editedAt || json.createdAt) + // this.lastModified = json.editedAt || json.createdAt ? DateTime.fromMillis(json.editedAt || json.createdAt) : null + this.lastModified = DateTime.fromMillis(1649319074) this.config = json.config this.position = json.config.position }) From ff3e185c434dc25d11bed45a936cd54fd3c34bd7 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 7 Apr 2022 11:34:59 +0200 Subject: [PATCH 65/87] feat(ui) - dashboard - wip --- .../CustomMetricPieChart/CustomMetricPieChart.tsx | 2 +- .../Dashboard/components/DashboardModal/DashboardModal.tsx | 1 - .../Dashboard/components/MetricsList/MetricsList.tsx | 3 ++- .../Dashboard/components/WidgetPreview/WidgetPreview.tsx | 2 -- frontend/app/components/Errors/Error/Trend.js | 1 - .../app/components/Session/Layout/ToolPanel/Performance.js | 1 - frontend/app/mstore/metricStore.ts | 2 -- frontend/app/mstore/types/dashboard.ts | 1 - 8 files changed, 3 insertions(+), 10 deletions(-) diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx index 91dbed7b5..a7e46eb20 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart/CustomMetricPieChart.tsx @@ -113,7 +113,7 @@ function CustomMetricPieChart(props: Props) { }} > {data && data.values && data.values.map((entry, index) => ( - + ))} diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index a5ba99dfb..4814ada0d 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -15,7 +15,6 @@ interface Props { } function DashboardModal(props) { const { history, siteId, dashboardId } = props; - console.log('DashboardModal', props); const { dashboardStore } = useStore(); const { hideModal } = useModal(); const dashboard = useObserver(() => dashboardStore.dashboardInstance); diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index 349386980..3916e0a05 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -14,7 +14,8 @@ function MetricsList(props: Props) { const filterList = (list) => { const filterRE = getRE(metricsSearch, 'i'); let _list = list.filter(w => { - return filterRE.test(w.name) || filterRE.test(w.metricType) || filterRE.test(w.owner) ; + const dashbaordNames = w.dashboards.map(d => d.name).join(' '); + return filterRE.test(w.name) || filterRE.test(w.metricType) || filterRE.test(w.owner) || filterRE.test(dashbaordNames); }); return _list } diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index 2c40dd0f0..cf2e9fe21 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -25,8 +25,6 @@ function WidgetPreview(props: Props) { metric.update({ ...changedDates, rangeName: changedDates.rangeValue }); } - console.log('view', metric.viewType) - return useObserver(() => (
diff --git a/frontend/app/components/Errors/Error/Trend.js b/frontend/app/components/Errors/Error/Trend.js index e6dcc1445..03b01909f 100644 --- a/frontend/app/components/Errors/Error/Trend.js +++ b/frontend/app/components/Errors/Error/Trend.js @@ -20,7 +20,6 @@ function Trend({ title = '', chart, onDateChange, timeFormat = 'hh:mm a' }) { if (!Array.isArray(chart)) return null const getDateFormat = val => { - console.log(val); const d = new Date(val); return (d.getMonth()+ 1) + '/' + d.getDate() } diff --git a/frontend/app/components/Session/Layout/ToolPanel/Performance.js b/frontend/app/components/Session/Layout/ToolPanel/Performance.js index 295085e46..f76c32f33 100644 --- a/frontend/app/components/Session/Layout/ToolPanel/Performance.js +++ b/frontend/app/components/Session/Layout/ToolPanel/Performance.js @@ -151,7 +151,6 @@ const NodesCountTooltip = ({ active, payload } ) => { const TICKS_COUNT = 10; function generateTicks(data: Array): Array { if (data.length === 0) return []; - console.log(data, data[0]) const minTime = data[0].time; const maxTime = data[data.length-1].time; diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index 455452adb..bb4815389 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -124,7 +124,6 @@ export default class MetricStore implements IMetricStore { } get paginatedList(): IWidget[] { - console.log('here...' + this.page) const start = (this.page - 1) * this.pageSize const end = start + this.pageSize return this.metrics.slice(start, end) @@ -187,7 +186,6 @@ export default class MetricStore implements IMetricStore { this.isLoading = true return metricService.getMetricChartData(metric) .then(data => { - console.log('data', data) // runInAction(() => { // metric.data = data // }) diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts index bd6551ba3..aefe7541f 100644 --- a/frontend/app/mstore/types/dashboard.ts +++ b/frontend/app/mstore/types/dashboard.ts @@ -139,7 +139,6 @@ export default class Dashboard implements IDashboard { } swapWidgetPosition(positionA, positionB) { - console.log('swapWidgetPosition', positionA, positionB) const widgetA = this.widgets[positionA] const widgetB = this.widgets[positionB] this.widgets[positionA] = widgetB From 39bffa21d655707eaa6d2f292bd2086e211ec9cf Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 12:23:02 +0200 Subject: [PATCH 66/87] feat(api): custom metrics & templates try schema --- api/schemas.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/schemas.py b/api/schemas.py index 65db116c0..158850618 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -818,7 +818,7 @@ class CustomMetricChartPayloadSchema(CustomMetricSessionsPayloadSchema): alias_generator = attribute_to_camel_case -class CreateCustomMetricsSchema(CustomMetricChartPayloadSchema): +class TryCustomMetricsPayloadSchema(CustomMetricChartPayloadSchema): name: str = Field(...) series: List[CustomMetricCreateSeriesSchema] = Field(...) is_public: bool = Field(default=True) @@ -859,6 +859,10 @@ class CreateCustomMetricsSchema(CustomMetricChartPayloadSchema): alias_generator = attribute_to_camel_case +class CreateCustomMetricsSchema(TryCustomMetricsPayloadSchema): + series: List[CustomMetricCreateSeriesSchema] = Field(..., min_items=1) + + class CustomMetricUpdateSeriesSchema(CustomMetricCreateSeriesSchema): series_id: Optional[int] = Field(None) From 9fcd55b879b293cc2fe1c1fc1d416fd3853b225b Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 12:30:07 +0200 Subject: [PATCH 67/87] feat(api): dashboard fixed create with metrics --- api/chalicelib/core/custom_metrics.py | 4 ++-- api/chalicelib/core/dashboards2.py | 2 +- api/schemas.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 77f902dcb..c6c93a7b1 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -9,7 +9,7 @@ from chalicelib.utils.TimeUTC import TimeUTC PIE_CHART_GROUP = 5 -def __try_live(project_id, data: schemas.CreateCustomMetricsSchema): +def __try_live(project_id, data: schemas.TryCustomMetricsPayloadSchema): results = [] for i, s in enumerate(data.series): s.filter.startDate = data.startDate @@ -42,7 +42,7 @@ def __try_live(project_id, data: schemas.CreateCustomMetricsSchema): return results -def merged_live(project_id, data: schemas.CreateCustomMetricsSchema): +def merged_live(project_id, data: schemas.TryCustomMetricsPayloadSchema): series_charts = __try_live(project_id=project_id, data=data) if data.view_type == schemas.MetricTimeseriesViewType.progress or data.metric_type == schemas.MetricType.table: return series_charts diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index ac382f3d7..d6c05f057 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -215,7 +215,7 @@ def pin_dashboard(project_id, user_id, dashboard_id): def create_metric_add_widget(project_id, user_id, dashboard_id, data: schemas.CreateCustomMetricsSchema): metric_id = custom_metrics.create(project_id=project_id, user_id=user_id, data=data, dashboard=True) return add_widget(project_id=project_id, user_id=user_id, dashboard_id=dashboard_id, - data=schemas.AddWidgetToDashboardPayloadSchema(metric_id=metric_id)) + data=schemas.AddWidgetToDashboardPayloadSchema(metricId=metric_id)) PREDEFINED = {schemas.TemplateKeys.count_sessions: dashboard.get_processed_sessions, diff --git a/api/schemas.py b/api/schemas.py index 158850618..6e54af018 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -906,7 +906,7 @@ class UpdateWidgetPayloadSchema(BaseModel): class AddWidgetToDashboardPayloadSchema(UpdateWidgetPayloadSchema): - metric_id: int = Field(default=None) + metric_id: int = Field(...) class Config: alias_generator = attribute_to_camel_case From de84d88d47ceae729b2307c0bcba7a97078fd55c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 12:30:29 +0200 Subject: [PATCH 68/87] feat(api): dashboard fixed create with metrics --- api/routers/subs/metrics.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 02eec811d..e2535433a 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -61,8 +61,7 @@ def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int, data: schemas.CreateCustomMetricsSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.create_metric_add_widget(project_id=projectId, user_id=context.user_id, - dashboard_id=dashboardId, - data=data)} + dashboard_id=dashboardId, data=data)} @app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"]) From 06b8366fab1cc1acaba758721f104eab695dacd2 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 12:48:02 +0200 Subject: [PATCH 69/87] feat(api): fixed /templates time conversion --- api/chalicelib/utils/TimeUTC.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/chalicelib/utils/TimeUTC.py b/api/chalicelib/utils/TimeUTC.py index bac7a027f..7befebb27 100644 --- a/api/chalicelib/utils/TimeUTC.py +++ b/api/chalicelib/utils/TimeUTC.py @@ -95,6 +95,8 @@ class TimeUTC: def datetime_to_timestamp(date): if date is None: return None + if isinstance(date, str): + return TimeUTC.human_to_timestamp(date, "%Y-%m-%dT%H:%M:%S.%f") return int(datetime.timestamp(date) * 1000) @staticmethod From f09a251c4478930b0b3ca472e71012e146a5229d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 13:28:01 +0200 Subject: [PATCH 70/87] feat(api): dashboard include series with templates --- api/chalicelib/core/dashboards2.py | 11 +++++++++-- api/chalicelib/utils/TimeUTC.py | 7 +++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index d6c05f057..37ec7a85e 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -14,8 +14,15 @@ CATEGORY_DESCRIPTION = { def get_templates(project_id, user_id): with pg_client.PostgresClient() as cur: pg_query = cur.mogrify(f"""SELECT category, jsonb_agg(metrics ORDER BY name) AS widgets - FROM metrics - WHERE deleted_at IS NULL AND (project_id ISNULL OR (project_id = %(project_id)s AND (is_public OR user_id= %(userId)s))) + FROM (SELECT * + FROM metrics LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index), '[]'::jsonb) AS series + FROM metric_series + WHERE metric_series.metric_id = metrics.metric_id + AND metric_series.deleted_at ISNULL + ) AS metric_series ON (TRUE) + WHERE deleted_at IS NULL + AND (project_id ISNULL OR (project_id = %(project_id)s AND (is_public OR user_id= %(userId)s))) + ) AS metrics GROUP BY category ORDER BY category;""", {"project_id": project_id, "userId": user_id}) cur.execute(pg_query) diff --git a/api/chalicelib/utils/TimeUTC.py b/api/chalicelib/utils/TimeUTC.py index 7befebb27..d399e1651 100644 --- a/api/chalicelib/utils/TimeUTC.py +++ b/api/chalicelib/utils/TimeUTC.py @@ -88,7 +88,7 @@ class TimeUTC: return datetime.utcfromtimestamp(ts // 1000).strftime(fmt) @staticmethod - def human_to_timestamp(ts, pattern): + def human_to_timestamp(ts, pattern="%Y-%m-%dT%H:%M:%S.%f"): return int(datetime.strptime(ts, pattern).timestamp() * 1000) @staticmethod @@ -96,7 +96,10 @@ class TimeUTC: if date is None: return None if isinstance(date, str): - return TimeUTC.human_to_timestamp(date, "%Y-%m-%dT%H:%M:%S.%f") + fp = date.find(".") + if fp > 0: + date += '0' * (6 - len(date[fp + 1:])) + date = datetime.fromisoformat(date) return int(datetime.timestamp(date) * 1000) @staticmethod From 14e8dde249ae42233fad6bc9e0fea9e33262d3f5 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 13:44:43 +0200 Subject: [PATCH 71/87] feat(api): pg_client reconnect if PG is not available --- api/chalicelib/utils/pg_client.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/api/chalicelib/utils/pg_client.py b/api/chalicelib/utils/pg_client.py index 6379bad1e..3d60dda5c 100644 --- a/api/chalicelib/utils/pg_client.py +++ b/api/chalicelib/utils/pg_client.py @@ -1,3 +1,4 @@ +import time from threading import Semaphore import psycopg2 @@ -37,9 +38,14 @@ class ORThreadedConnectionPool(psycopg2.pool.ThreadedConnectionPool): postgreSQL_pool: ORThreadedConnectionPool = None +RETRY_MAX = config("PG_RETRY_MAX", cast=int, default=50) +RETRY_INTERVAL = config("PG_RETRY_INTERVAL", cast=int, default=2) +RETRY = 0 + def make_pool(): global postgreSQL_pool + global RETRY if postgreSQL_pool is not None: try: postgreSQL_pool.closeall() @@ -51,7 +57,13 @@ def make_pool(): print("Connection pool created successfully") except (Exception, psycopg2.DatabaseError) as error: print("Error while connecting to PostgreSQL", error) - raise error + if RETRY < RETRY_MAX: + RETRY += 1 + print(f"waiting for {RETRY_INTERVAL}s before retry n°{RETRY}") + time.sleep(RETRY_INTERVAL) + make_pool() + else: + raise error make_pool() From f423e9c9cfb30a635e33820c24fef40ca0a9ff0d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 13:48:07 +0200 Subject: [PATCH 72/87] feat(api): pg_client reconnect config --- api/.env.default | 2 ++ ee/api/.env.default | 2 ++ 2 files changed, 4 insertions(+) diff --git a/api/.env.default b/api/.env.default index 6ae959a7d..0d4f8130c 100644 --- a/api/.env.default +++ b/api/.env.default @@ -37,6 +37,8 @@ pg_port=5432 pg_user=postgres pg_timeout=30 pg_minconn=45 +PG_RETRY_MAX=50 +PG_RETRY_INTERVAL=2 put_S3_TTL=20 sentryURL= sessions_bucket=mobs diff --git a/ee/api/.env.default b/ee/api/.env.default index 28f46f273..778a8f32c 100644 --- a/ee/api/.env.default +++ b/ee/api/.env.default @@ -46,6 +46,8 @@ pg_port=5432 pg_user=postgres pg_timeout=30 pg_minconn=45 +PG_RETRY_MAX=50 +PG_RETRY_INTERVAL=2 put_S3_TTL=20 sentryURL= sessions_bucket=mobs From ed51fc09cb950b53b2f49d77ac2f1488d9c4a0c2 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 14:21:50 +0200 Subject: [PATCH 73/87] feat(api): dashboard category description feat(db): changed metrics to support overview metric-type --- api/chalicelib/core/authorizers.py | 4 +- api/chalicelib/core/dashboards2.py | 3 +- api/routers/subs/metrics.py | 2 +- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 49 ++++++++++--------- .../db/init_dbs/postgresql/init_schema.sql | 45 ++++++++--------- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 49 ++++++++++--------- .../db/init_dbs/postgresql/init_schema.sql | 45 ++++++++--------- 7 files changed, 101 insertions(+), 96 deletions(-) diff --git a/api/chalicelib/core/authorizers.py b/api/chalicelib/core/authorizers.py index 33a859cc8..899fd046f 100644 --- a/api/chalicelib/core/authorizers.py +++ b/api/chalicelib/core/authorizers.py @@ -13,9 +13,9 @@ def jwt_authorizer(token): try: payload = jwt.decode( token[1], - config("jwt_secret"), + "", algorithms=config("jwt_algorithm"), - audience=[f"plugin:{helper.get_stage_name()}", f"front:{helper.get_stage_name()}"] + audience=[ f"front:default-foss"] ) except jwt.ExpiredSignatureError: print("! JWT Expired signature") diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 37ec7a85e..868bb9c4a 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -7,7 +7,8 @@ from chalicelib.utils import pg_client from chalicelib.utils.TimeUTC import TimeUTC CATEGORY_DESCRIPTION = { - 'categ1': 'lorem', + 'overview': 'lorem ipsum', + 'custom': 'lorem cusipsum', } diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index e2535433a..0a806b146 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -100,7 +100,7 @@ def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_c @app.put('/{projectId}/metrics/try', tags=["dashboard"]) @app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"]) @app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"]) -def try_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...), +def try_custom_metric(projectId: int, data: schemas.TryCustomMetricsPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": custom_metrics.merged_live(project_id=projectId, data=data)} diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 15d5f314b..1472e1779 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -48,33 +48,34 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets config jsonb NOT NULL DEFAULT '{}'::jsonb ); +COMMIT; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; +ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'overview'; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu') +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key, metric_type) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'overview'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'overview'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'overview'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'overview'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'overview'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'overview'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'overview'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'overview'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'overview'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'overview'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'overview'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'overview'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'overview'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'overview'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'overview'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'overview'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'overview'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'overview'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'overview') ON CONFLICT (key) DO UPDATE SET name=excluded.name, category=excluded.category, config=excluded.config, is_predefined=excluded.is_predefined, is_template=excluded.is_template, - is_public=excluded.is_public; - -COMMIT; -ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; \ No newline at end of file + is_public=excluded.is_public, + metric_type=excluded.metric_type; \ No newline at end of file diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 506853ffc..ea60c70ec 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -788,7 +788,7 @@ $$ CREATE INDEX IF NOT EXISTS traces_user_id_idx ON traces (user_id); CREATE INDEX IF NOT EXISTS traces_tenant_id_idx ON traces (tenant_id); - CREATE TYPE metric_type AS ENUM ('timeseries','table'); + CREATE TYPE metric_type AS ENUM ('timeseries','table', 'overview'); CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart'); CREATE TABLE IF NOT EXISTS metrics ( @@ -1257,31 +1257,32 @@ $$ $$ LANGUAGE plpgsql; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu') +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key, metric_type) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'overview'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'overview'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'overview'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'overview'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'overview'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'overview'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'overview'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'overview'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'overview'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'overview'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'overview'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'overview'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'overview'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'overview'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'overview'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'overview'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'overview'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'overview'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'overview') ON CONFLICT (key) DO UPDATE SET name=excluded.name, category=excluded.category, config=excluded.config, is_predefined=excluded.is_predefined, is_template=excluded.is_template, - is_public=excluded.is_public; + is_public=excluded.is_public, + metric_type=excluded.metric_type; COMMIT; \ No newline at end of file diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 1a3a918ed..a511090d1 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -48,33 +48,34 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets config jsonb NOT NULL DEFAULT '{}'::jsonb ); +COMMIT; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; +ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'overview'; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu') +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key, metric_type) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'overview'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'overview'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'overview'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'overview'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'overview'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'overview'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'overview'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'overview'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'overview'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'overview'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'overview'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'overview'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'overview'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'overview'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'overview'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'overview'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'overview'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'overview'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'overview') ON CONFLICT (key) DO UPDATE SET name=excluded.name, category=excluded.category, config=excluded.config, is_predefined=excluded.is_predefined, is_template=excluded.is_template, - is_public=excluded.is_public; - -COMMIT; -ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; \ No newline at end of file + is_public=excluded.is_public, + metric_type=excluded.metric_type; \ No newline at end of file diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 8a0d2e807..838aa53cd 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -935,7 +935,7 @@ $$ CREATE INDEX jobs_start_at_idx ON jobs (start_at); CREATE INDEX jobs_project_id_idx ON jobs (project_id); - CREATE TYPE metric_type AS ENUM ('timeseries','table'); + CREATE TYPE metric_type AS ENUM ('timeseries','table', 'overview'); CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart'); CREATE TABLE metrics ( @@ -1048,31 +1048,32 @@ $$ $$ LANGUAGE plpgsql; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu') +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key, metric_type) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'overview'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'overview'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'overview'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'overview'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'overview'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'overview'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'overview'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'overview'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'overview'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'overview'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'overview'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'overview'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'overview'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'overview'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'overview'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'overview'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'overview'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'overview'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'overview') ON CONFLICT (key) DO UPDATE SET name=excluded.name, category=excluded.category, config=excluded.config, is_predefined=excluded.is_predefined, is_template=excluded.is_template, - is_public=excluded.is_public; + is_public=excluded.is_public, + metric_type=excluded.metric_type; COMMIT; \ No newline at end of file From 48052317954c576fe8fae302553ac2b0ace7f2df Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 15:39:51 +0200 Subject: [PATCH 74/87] feat(api): dashboard defined new categories feat(api): changed Templates schemas feat(db): changed metrics structure feat(db): changed metric_view_type --- api/chalicelib/core/dashboards2.py | 45 ++++---- api/routers/subs/dashboard.py | 42 ++++---- api/schemas.py | 4 +- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 80 +++++++------- .../db/init_dbs/postgresql/init_schema.sql | 102 +++++++++--------- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 79 +++++++------- .../db/init_dbs/postgresql/init_schema.sql | 102 +++++++++--------- 7 files changed, 238 insertions(+), 216 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 868bb9c4a..ed9a17d76 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -9,6 +9,9 @@ from chalicelib.utils.TimeUTC import TimeUTC CATEGORY_DESCRIPTION = { 'overview': 'lorem ipsum', 'custom': 'lorem cusipsum', + 'errors': 'lorem erripsum', + 'performance': 'lorem perfipsum', + 'resources': 'lorem resipsum' } @@ -226,29 +229,29 @@ def create_metric_add_widget(project_id, user_id, dashboard_id, data: schemas.Cr data=schemas.AddWidgetToDashboardPayloadSchema(metricId=metric_id)) -PREDEFINED = {schemas.TemplateKeys.count_sessions: dashboard.get_processed_sessions, - schemas.TemplateKeys.avg_image_load_time: dashboard.get_application_activity_avg_image_load_time, - schemas.TemplateKeys.avg_page_load_time: dashboard.get_application_activity_avg_page_load_time, - schemas.TemplateKeys.avg_request_load_time: dashboard.get_application_activity_avg_request_load_time, - schemas.TemplateKeys.avg_dom_content_load_start: dashboard.get_page_metrics_avg_dom_content_load_start, - schemas.TemplateKeys.avg_first_contentful_pixel: dashboard.get_page_metrics_avg_first_contentful_pixel, - schemas.TemplateKeys.avg_visited_pages: dashboard.get_user_activity_avg_visited_pages, - schemas.TemplateKeys.avg_session_duration: dashboard.get_user_activity_avg_session_duration, - schemas.TemplateKeys.avg_pages_dom_buildtime: dashboard.get_pages_dom_build_time, - schemas.TemplateKeys.avg_pages_response_time: dashboard.get_pages_response_time, - schemas.TemplateKeys.avg_response_time: dashboard.get_top_metrics_avg_response_time, - schemas.TemplateKeys.avg_first_paint: dashboard.get_top_metrics_avg_first_paint, - schemas.TemplateKeys.avg_dom_content_loaded: dashboard.get_top_metrics_avg_dom_content_loaded, - schemas.TemplateKeys.avg_till_first_bit: dashboard.get_top_metrics_avg_till_first_bit, - schemas.TemplateKeys.avg_time_to_interactive: dashboard.get_top_metrics_avg_time_to_interactive, - schemas.TemplateKeys.count_requests: dashboard.get_top_metrics_count_requests, - schemas.TemplateKeys.avg_time_to_render: dashboard.get_time_to_render, - schemas.TemplateKeys.avg_used_js_heap_size: dashboard.get_memory_consumption, - schemas.TemplateKeys.avg_cpu: dashboard.get_avg_cpu, - schemas.TemplateKeys.avg_fps: dashboard.get_avg_fps} +PREDEFINED = {schemas.TemplatePredefinedKeys.count_sessions: dashboard.get_processed_sessions, + schemas.TemplatePredefinedKeys.avg_image_load_time: dashboard.get_application_activity_avg_image_load_time, + schemas.TemplatePredefinedKeys.avg_page_load_time: dashboard.get_application_activity_avg_page_load_time, + schemas.TemplatePredefinedKeys.avg_request_load_time: dashboard.get_application_activity_avg_request_load_time, + schemas.TemplatePredefinedKeys.avg_dom_content_load_start: dashboard.get_page_metrics_avg_dom_content_load_start, + schemas.TemplatePredefinedKeys.avg_first_contentful_pixel: dashboard.get_page_metrics_avg_first_contentful_pixel, + schemas.TemplatePredefinedKeys.avg_visited_pages: dashboard.get_user_activity_avg_visited_pages, + schemas.TemplatePredefinedKeys.avg_session_duration: dashboard.get_user_activity_avg_session_duration, + schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime: dashboard.get_pages_dom_build_time, + schemas.TemplatePredefinedKeys.avg_pages_response_time: dashboard.get_pages_response_time, + schemas.TemplatePredefinedKeys.avg_response_time: dashboard.get_top_metrics_avg_response_time, + schemas.TemplatePredefinedKeys.avg_first_paint: dashboard.get_top_metrics_avg_first_paint, + schemas.TemplatePredefinedKeys.avg_dom_content_loaded: dashboard.get_top_metrics_avg_dom_content_loaded, + schemas.TemplatePredefinedKeys.avg_till_first_bit: dashboard.get_top_metrics_avg_till_first_bit, + schemas.TemplatePredefinedKeys.avg_time_to_interactive: dashboard.get_top_metrics_avg_time_to_interactive, + schemas.TemplatePredefinedKeys.count_requests: dashboard.get_top_metrics_count_requests, + schemas.TemplatePredefinedKeys.avg_time_to_render: dashboard.get_time_to_render, + schemas.TemplatePredefinedKeys.avg_used_js_heap_size: dashboard.get_memory_consumption, + schemas.TemplatePredefinedKeys.avg_cpu: dashboard.get_avg_cpu, + schemas.TemplatePredefinedKeys.avg_fps: dashboard.get_avg_fps} -def get_predefined_metric(key: schemas.TemplateKeys, project_id: int, data: dict): +def get_predefined_metric(key: schemas.TemplatePredefinedKeys, project_id: int, data: dict): return PREDEFINED.get(key, lambda *args: None)(project_id=project_id, **data) diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 0832be4b8..e2d4ba268 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -342,7 +342,7 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body {"key": "avg_time_to_render", "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, {"key": "avg_used_js_heap_size", "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, {"key": "avg_cpu", "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_fps, "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + {"key": schemas.TemplatePredefinedKeys.avg_fps, "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} ] results = sorted(results, key=lambda r: r["key"]) return {"data": results} @@ -352,45 +352,45 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body @app.get('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"]) def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): results = [ - {"key": schemas.TemplateKeys.count_sessions, + {"key": schemas.TemplatePredefinedKeys.count_sessions, "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_image_load_time, + {"key": schemas.TemplatePredefinedKeys.avg_image_load_time, "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_page_load_time, + {"key": schemas.TemplatePredefinedKeys.avg_page_load_time, "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_request_load_time, + {"key": schemas.TemplatePredefinedKeys.avg_request_load_time, "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_dom_content_load_start, + {"key": schemas.TemplatePredefinedKeys.avg_dom_content_load_start, "data": dashboard.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_first_contentful_pixel, + {"key": schemas.TemplatePredefinedKeys.avg_first_contentful_pixel, "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_visited_pages, + {"key": schemas.TemplatePredefinedKeys.avg_visited_pages, "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_session_duration, + {"key": schemas.TemplatePredefinedKeys.avg_session_duration, "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_pages_dom_buildtime, + {"key": schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime, "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_pages_response_time, + {"key": schemas.TemplatePredefinedKeys.avg_pages_response_time, "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_response_time, + {"key": schemas.TemplatePredefinedKeys.avg_response_time, "data": dashboard.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_first_paint, + {"key": schemas.TemplatePredefinedKeys.avg_first_paint, "data": dashboard.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_dom_content_loaded, + {"key": schemas.TemplatePredefinedKeys.avg_dom_content_loaded, "data": dashboard.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_till_first_bit, + {"key": schemas.TemplatePredefinedKeys.avg_till_first_bit, "data": dashboard.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_time_to_interactive, + {"key": schemas.TemplatePredefinedKeys.avg_time_to_interactive, "data": dashboard.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.count_requests, + {"key": schemas.TemplatePredefinedKeys.count_requests, "data": dashboard.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_time_to_render, + {"key": schemas.TemplatePredefinedKeys.avg_time_to_render, "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_used_js_heap_size, + {"key": schemas.TemplatePredefinedKeys.avg_used_js_heap_size, "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_cpu, + {"key": schemas.TemplatePredefinedKeys.avg_cpu, "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, - {"key": schemas.TemplateKeys.avg_fps, + {"key": schemas.TemplatePredefinedKeys.avg_fps, "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} ] results = sorted(results, key=lambda r: r["key"]) diff --git a/api/schemas.py b/api/schemas.py index 6e54af018..7dd7774ba 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -913,7 +913,7 @@ class AddWidgetToDashboardPayloadSchema(UpdateWidgetPayloadSchema): # these values should match the keys in metrics table -class TemplateKeys(str, Enum): +class TemplatePredefinedKeys(str, Enum): count_sessions = "count_sessions" avg_request_load_time = "avg_request_load_time" avg_page_load_time = "avg_page_load_time" @@ -939,7 +939,7 @@ class TemplateKeys(str, Enum): class CustomMetricAndTemplate(BaseModel): is_template: bool = Field(...) project_id: Optional[int] = Field(...) - key: Optional[TemplateKeys] = Field(...) + key: Optional[TemplatePredefinedKeys] = Field(...) class Config: alias_generator = attribute_to_camel_case diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 1472e1779..1ba9cc0af 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -24,17 +24,17 @@ ALTER TABLE IF EXISTS metrics DROP CONSTRAINT IF EXISTS unique_key; ALTER TABLE IF EXISTS metrics - ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', - ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS key text NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, + ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', + ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS predefined_key text NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, ALTER COLUMN project_id DROP NOT NULL, ADD CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), - ADD CONSTRAINT unique_key UNIQUE (key); + ADD CONSTRAINT unique_key UNIQUE (predefined_key); @@ -50,32 +50,40 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets COMMIT; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; -ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'overview'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'barChart'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'stackedBarChart'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'stackedBarLineChart'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'overview'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'map'; +ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'predefined'; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key, metric_type) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'overview'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'overview'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'overview'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'overview'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'overview'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'overview'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'overview'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'overview'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'overview'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'overview'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'overview'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'overview'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'overview'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'overview'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'overview'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'overview'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'overview'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'overview') -ON CONFLICT (key) DO UPDATE SET name=excluded.name, - category=excluded.category, - config=excluded.config, - is_predefined=excluded.is_predefined, - is_template=excluded.is_template, - is_public=excluded.is_public, - metric_type=excluded.metric_type; \ No newline at end of file + +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'predefined', 'overview'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview') +ON CONFLICT (predefined_key) DO UPDATE + SET name=excluded.name, + category=excluded.category, + config=excluded.config, + is_predefined=excluded.is_predefined, + is_template=excluded.is_template, + is_public=excluded.is_public, + metric_type=excluded.metric_type, + view_type=excluded.view_type; \ No newline at end of file diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index ea60c70ec..6a0258af7 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -788,33 +788,33 @@ $$ CREATE INDEX IF NOT EXISTS traces_user_id_idx ON traces (user_id); CREATE INDEX IF NOT EXISTS traces_tenant_id_idx ON traces (tenant_id); - CREATE TYPE metric_type AS ENUM ('timeseries','table', 'overview'); - CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart'); + CREATE TYPE metric_type AS ENUM ('timeseries','table', 'predefined'); + CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart','barChart','stackedBarChart','stackedBarLineChart','overview','map'); CREATE TABLE IF NOT EXISTS metrics ( - metric_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NULL REFERENCES projects (project_id) ON DELETE CASCADE, - user_id integer REFERENCES users (user_id) ON DELETE SET NULL, - name text NOT NULL, - is_public boolean NOT NULL DEFAULT FALSE, - active boolean NOT NULL DEFAULT TRUE, - created_at timestamp default timezone('utc'::text, now()) not null, - deleted_at timestamp, - edited_at timestamp, - metric_type metric_type NOT NULL DEFAULT 'timeseries', - view_type metric_view_type NOT NULL DEFAULT 'lineChart', - metric_of text NOT NULL DEFAULT 'sessionCount', - metric_value text[] NOT NULL DEFAULT '{}'::text[], - metric_format text, - category text NULL DEFAULT 'custom', - is_pinned boolean NOT NULL DEFAULT FALSE, - is_predefined boolean NOT NULL DEFAULT FALSE, - is_template boolean NOT NULL DEFAULT FALSE, - key text NULL DEFAULT NULL, - config jsonb NOT NULL DEFAULT '{}'::jsonb, + metric_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + project_id integer NULL REFERENCES projects (project_id) ON DELETE CASCADE, + user_id integer REFERENCES users (user_id) ON DELETE SET NULL, + name text NOT NULL, + is_public boolean NOT NULL DEFAULT FALSE, + active boolean NOT NULL DEFAULT TRUE, + created_at timestamp default timezone('utc'::text, now()) not null, + deleted_at timestamp, + edited_at timestamp, + metric_type metric_type NOT NULL DEFAULT 'timeseries', + view_type metric_view_type NOT NULL DEFAULT 'lineChart', + metric_of text NOT NULL DEFAULT 'sessionCount', + metric_value text[] NOT NULL DEFAULT '{}'::text[], + metric_format text, + category text NULL DEFAULT 'custom', + is_pinned boolean NOT NULL DEFAULT FALSE, + is_predefined boolean NOT NULL DEFAULT FALSE, + is_template boolean NOT NULL DEFAULT FALSE, + predefined_key text NULL DEFAULT NULL, + config jsonb NOT NULL DEFAULT '{}'::jsonb, CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), - CONSTRAINT unique_key UNIQUE (key) + CONSTRAINT unique_key UNIQUE (predefined_key) ); CREATE INDEX IF NOT EXISTS metrics_user_id_is_public_idx ON public.metrics (user_id, is_public); CREATE TABLE IF NOT EXISTS metric_series @@ -1257,32 +1257,34 @@ $$ $$ LANGUAGE plpgsql; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key, metric_type) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'overview'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'overview'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'overview'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'overview'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'overview'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'overview'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'overview'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'overview'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'overview'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'overview'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'overview'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'overview'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'overview'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'overview'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'overview'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'overview'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'overview'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'overview') -ON CONFLICT (key) DO UPDATE SET name=excluded.name, - category=excluded.category, - config=excluded.config, - is_predefined=excluded.is_predefined, - is_template=excluded.is_template, - is_public=excluded.is_public, - metric_type=excluded.metric_type; +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'predefined', 'overview'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview') +ON CONFLICT (predefined_key) DO UPDATE + SET name=excluded.name, + category=excluded.category, + config=excluded.config, + is_predefined=excluded.is_predefined, + is_template=excluded.is_template, + is_public=excluded.is_public, + metric_type=excluded.metric_type, + view_type=excluded.view_type; COMMIT; \ No newline at end of file diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index a511090d1..5afd50a77 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -24,17 +24,17 @@ ALTER TABLE IF EXISTS metrics DROP CONSTRAINT IF EXISTS unique_key; ALTER TABLE IF EXISTS metrics - ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', - ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS key text NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, + ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', + ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS predefined_key text NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, ALTER COLUMN project_id DROP NOT NULL, ADD CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), - ADD CONSTRAINT unique_key UNIQUE (key); + ADD CONSTRAINT unique_key UNIQUE (predefined_key); @@ -50,32 +50,39 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets COMMIT; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; -ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'overview'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'barChart'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'stackedBarChart'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'stackedBarLineChart'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'overview'; +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'map'; +ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'predefined'; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key, metric_type) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'overview'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'overview'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'overview'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'overview'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'overview'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'overview'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'overview'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'overview'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'overview'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'overview'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'overview'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'overview'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'overview'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'overview'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'overview'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'overview'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'overview'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'overview') -ON CONFLICT (key) DO UPDATE SET name=excluded.name, - category=excluded.category, - config=excluded.config, - is_predefined=excluded.is_predefined, - is_template=excluded.is_template, - is_public=excluded.is_public, - metric_type=excluded.metric_type; \ No newline at end of file +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'predefined', 'overview'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview') +ON CONFLICT (predefined_key) DO UPDATE + SET name=excluded.name, + category=excluded.category, + config=excluded.config, + is_predefined=excluded.is_predefined, + is_template=excluded.is_template, + is_public=excluded.is_public, + metric_type=excluded.metric_type, + view_type=excluded.view_type; \ No newline at end of file diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 838aa53cd..3278a48b7 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -935,33 +935,33 @@ $$ CREATE INDEX jobs_start_at_idx ON jobs (start_at); CREATE INDEX jobs_project_id_idx ON jobs (project_id); - CREATE TYPE metric_type AS ENUM ('timeseries','table', 'overview'); - CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart'); + CREATE TYPE metric_type AS ENUM ('timeseries','table', 'predefined'); + CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart','barChart','stackedBarChart','stackedBarLineChart','overview','map'); CREATE TABLE metrics ( - metric_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NULL REFERENCES projects (project_id) ON DELETE CASCADE, - user_id integer REFERENCES users (user_id) ON DELETE SET NULL, - name text NOT NULL, - is_public boolean NOT NULL DEFAULT FALSE, - active boolean NOT NULL DEFAULT TRUE, - created_at timestamp default timezone('utc'::text, now()) not null, - deleted_at timestamp, - edited_at timestamp, - metric_type metric_type NOT NULL DEFAULT 'timeseries', - view_type metric_view_type NOT NULL DEFAULT 'lineChart', - metric_of text NOT NULL DEFAULT 'sessionCount', - metric_value text[] NOT NULL DEFAULT '{}'::text[], - metric_format text, - category text NULL DEFAULT 'custom', - is_pinned boolean NOT NULL DEFAULT FALSE, - is_predefined boolean NOT NULL DEFAULT FALSE, - is_template boolean NOT NULL DEFAULT FALSE, - key text NULL DEFAULT NULL, - config jsonb NOT NULL DEFAULT '{}'::jsonb, + metric_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + project_id integer NULL REFERENCES projects (project_id) ON DELETE CASCADE, + user_id integer REFERENCES users (user_id) ON DELETE SET NULL, + name text NOT NULL, + is_public boolean NOT NULL DEFAULT FALSE, + active boolean NOT NULL DEFAULT TRUE, + created_at timestamp default timezone('utc'::text, now()) not null, + deleted_at timestamp, + edited_at timestamp, + metric_type metric_type NOT NULL DEFAULT 'timeseries', + view_type metric_view_type NOT NULL DEFAULT 'lineChart', + metric_of text NOT NULL DEFAULT 'sessionCount', + metric_value text[] NOT NULL DEFAULT '{}'::text[], + metric_format text, + category text NULL DEFAULT 'custom', + is_pinned boolean NOT NULL DEFAULT FALSE, + is_predefined boolean NOT NULL DEFAULT FALSE, + is_template boolean NOT NULL DEFAULT FALSE, + predefined_key text NULL DEFAULT NULL, + config jsonb NOT NULL DEFAULT '{}'::jsonb, CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), - CONSTRAINT unique_key UNIQUE (key) + CONSTRAINT unique_key UNIQUE (predefined_key) ); CREATE INDEX metrics_user_id_is_public_idx ON public.metrics (user_id, is_public); @@ -1048,32 +1048,34 @@ $$ $$ LANGUAGE plpgsql; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key, metric_type) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'overview'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'overview'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'overview'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'overview'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'overview'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'overview'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'overview'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'overview'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'overview'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'overview'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'overview'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'overview'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'overview'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'overview'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'overview'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'overview'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'overview'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'overview') -ON CONFLICT (key) DO UPDATE SET name=excluded.name, - category=excluded.category, - config=excluded.config, - is_predefined=excluded.is_predefined, - is_template=excluded.is_template, - is_public=excluded.is_public, - metric_type=excluded.metric_type; +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) +VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), + ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), + ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), + ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), + ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), + ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), + ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), + ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), + ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), + ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), + ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), + ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), + ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), + ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'predefined', 'overview'), + ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), + ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), + ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), + ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview') +ON CONFLICT (predefined_key) DO UPDATE + SET name=excluded.name, + category=excluded.category, + config=excluded.config, + is_predefined=excluded.is_predefined, + is_template=excluded.is_template, + is_public=excluded.is_public, + metric_type=excluded.metric_type, + view_type=excluded.view_type; COMMIT; \ No newline at end of file From 91e7a4103e62ddde31283683fc269dc3e8798c01 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 15:45:44 +0200 Subject: [PATCH 75/87] feat(api): dashboard fixed new predefined key issue --- api/chalicelib/core/custom_metrics.py | 21 ++++++++++++--------- api/chalicelib/core/dashboards2.py | 8 +++++--- api/schemas.py | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index c6c93a7b1..a43de6903 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -309,24 +309,27 @@ def get(metric_id, project_id, user_id, flatten=True): return helper.dict_to_camel_case(row) -def get_with_template(metric_id, project_id, user_id): +def get_with_template(metric_id, project_id, user_id, include_dashboard=True): with pg_client.PostgresClient() as cur: + sub_query="" + if include_dashboard: + sub_query="""LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards + FROM (SELECT dashboard_id, name, is_public + FROM dashboards + WHERE deleted_at ISNULL + AND project_id = %(project_id)s + AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards + ) AS connected_dashboards ON (TRUE)""" cur.execute( cur.mogrify( - """SELECT * + f"""SELECT * FROM metrics LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index),'[]'::jsonb) AS series FROM metric_series WHERE metric_series.metric_id = metrics.metric_id AND metric_series.deleted_at ISNULL ) AS metric_series ON (TRUE) - LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards - FROM (SELECT dashboard_id, name, is_public - FROM dashboards - WHERE deleted_at ISNULL - AND project_id = %(project_id)s - AND ((user_id = %(user_id)s OR is_public))) AS connected_dashboards - ) AS connected_dashboards ON (TRUE) + {sub_query} WHERE (metrics.project_id = %(project_id)s OR metrics.project_id ISNULL) AND metrics.deleted_at ISNULL AND (metrics.user_id = %(user_id)s OR metrics.is_public) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index ed9a17d76..aa188bb1f 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -256,12 +256,14 @@ def get_predefined_metric(key: schemas.TemplatePredefinedKeys, project_id: int, def make_chart_metrics(project_id, user_id, metric_id, data: schemas.CustomMetricChartPayloadSchema): - raw_metric = custom_metrics.get_with_template(metric_id=metric_id, project_id=project_id, user_id=user_id) + raw_metric = custom_metrics.get_with_template(metric_id=metric_id, project_id=project_id, user_id=user_id, + include_dashboard=False) if raw_metric is None: return None + print(raw_metric) metric = schemas.CustomMetricAndTemplate = schemas.CustomMetricAndTemplate.parse_obj(raw_metric) if metric.is_template: - return get_predefined_metric(key=metric.key, project_id=project_id, data=data.dict()) + return get_predefined_metric(key=metric.predefined_key, project_id=project_id, data=data.dict()) else: return custom_metrics.make_chart(project_id=project_id, user_id=user_id, metric_id=metric_id, data=data, metric=raw_metric) @@ -273,7 +275,7 @@ def make_chart_widget(dashboard_id, project_id, user_id, widget_id, data: schema return None metric = schemas.CustomMetricAndTemplate = schemas.CustomMetricAndTemplate.parse_obj(raw_metric) if metric.is_template: - return get_predefined_metric(key=metric.key, project_id=project_id, data=data.dict()) + return get_predefined_metric(key=metric.predefined_key, project_id=project_id, data=data.dict()) else: return custom_metrics.make_chart(project_id=project_id, user_id=user_id, metric_id=raw_metric["metricId"], data=data, metric=raw_metric) diff --git a/api/schemas.py b/api/schemas.py index 7dd7774ba..e518b9914 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -939,7 +939,7 @@ class TemplatePredefinedKeys(str, Enum): class CustomMetricAndTemplate(BaseModel): is_template: bool = Field(...) project_id: Optional[int] = Field(...) - key: Optional[TemplatePredefinedKeys] = Field(...) + predefined_key: Optional[TemplatePredefinedKeys] = Field(...) class Config: alias_generator = attribute_to_camel_case From f26fbc9a71a40b4509575e487e4e570a5ba00314 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 16:25:50 +0200 Subject: [PATCH 76/87] feat(db): defined new predefined templates (errors) feat(api): support for errors predefined templates feat(api): fixed update widget config --- api/chalicelib/core/dashboard.py | 27 ++++++++++++++----- api/chalicelib/core/dashboards2.py | 14 +++++++--- api/schemas.py | 16 +++++++++++ .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 9 ++++++- .../db/init_dbs/postgresql/init_schema.sql | 9 ++++++- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 9 ++++++- .../db/init_dbs/postgresql/init_schema.sql | 9 ++++++- 7 files changed, 80 insertions(+), 13 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 6de949047..362e4c491 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -170,7 +170,7 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) count = cur.fetchone()["count"] results["progress"] = helper.__progress(old_val=count, new_val=results["value"]) - + results["unit"] = schemas.TemplatePredefinedUnits.count return results @@ -998,6 +998,7 @@ def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=- cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return row @@ -1156,7 +1157,7 @@ def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1 WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] - return {"value": avg, "chart": rows} + return {"value": avg, "chart": rows, "unit": schemas.TemplatePredefinedUnits.millisecond} def get_pages_response_time_distribution(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1378,6 +1379,7 @@ def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "endTimestamp": endTimestamp, "value": url, **__get_constraint_values(args)} cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return row @@ -1448,7 +1450,7 @@ def get_memory_consumption(project_id, startTimestamp=TimeUTC.now(delta_days=-1) WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] - return {"value": avg, "chart": helper.list_to_camel_case(rows)} + return {"value": avg, "chart": helper.list_to_camel_case(rows), "unit": schemas.TemplatePredefinedUnits.memory} def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1480,7 +1482,8 @@ def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] - return {"value": avg, "chart": helper.list_to_camel_case(rows)} + return {"value": avg, "chart": helper.list_to_camel_case(rows), + "unit": schemas.TemplatePredefinedUnits.percentage} def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1512,7 +1515,7 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] - return {"value": avg, "chart": helper.list_to_camel_case(rows)} + return {"value": avg, "chart": helper.list_to_camel_case(rows), "unit": schemas.TemplatePredefinedUnits.frame} def get_crashes(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -2269,7 +2272,7 @@ def get_application_activity_avg_image_load_time(project_id, startTimestamp=Time row = __get_application_activity_avg_image_load_time(cur, project_id, startTimestamp, endTimestamp, **args) previous = helper.dict_to_camel_case(row) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) - + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2345,6 +2348,7 @@ def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeU row = __get_application_activity_avg_page_load_time(cur, project_id, startTimestamp, endTimestamp, **args) previous = helper.dict_to_camel_case(row) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2414,6 +2418,7 @@ def get_application_activity_avg_request_load_time(project_id, startTimestamp=Ti row = __get_application_activity_avg_request_load_time(cur, project_id, startTimestamp, endTimestamp, **args) previous = helper.dict_to_camel_case(row) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2475,6 +2480,7 @@ def get_page_metrics_avg_dom_content_load_start(project_id, startTimestamp=TimeU if len(rows) > 0: previous = helper.dict_to_camel_case(rows[0]) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2509,6 +2515,7 @@ def get_page_metrics_avg_first_contentful_pixel(project_id, startTimestamp=TimeU if len(rows) > 0: previous = helper.dict_to_camel_case(rows[0]) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2542,6 +2549,7 @@ def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(d previous = helper.dict_to_camel_case(row) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["unit"] = schemas.TemplatePredefinedUnits.count return results @@ -2572,6 +2580,7 @@ def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.no previous = helper.dict_to_camel_case(row) results["progress"] = helper.__progress(old_val=previous["value"], new_val=results["value"]) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2609,6 +2618,7 @@ def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(del "endTimestamp": endTimestamp, "value": value, **__get_constraint_values(args)})) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) @@ -2631,6 +2641,7 @@ def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta "endTimestamp": endTimestamp, "value": value, **__get_constraint_values(args)})) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) @@ -2653,6 +2664,7 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no "endTimestamp": endTimestamp, "value": value, **__get_constraint_values(args)})) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) @@ -2675,6 +2687,7 @@ def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(de "endTimestamp": endTimestamp, "value": value, **__get_constraint_values(args)})) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) @@ -2697,6 +2710,7 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n "endTimestamp": endTimestamp, "value": value, **__get_constraint_values(args)})) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) @@ -2715,4 +2729,5 @@ def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_ "endTimestamp": endTimestamp, "value": value, **__get_constraint_values(args)})) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.count return helper.dict_to_camel_case(row) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index aa188bb1f..594db2fd0 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -191,9 +191,10 @@ def update_widget(project_id, user_id, dashboard_id, widget_id, data: schemas.Up pg_query = """UPDATE dashboard_widgets SET config= %(config)s WHERE dashboard_id=%(dashboard_id)s AND widget_id=%(widget_id)s - RETURNINIG *;""" + RETURNING *;""" params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, "widget_id": widget_id, **data.dict()} + params["config"] = json.dumps(data.config) cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() return helper.dict_to_camel_case(row) @@ -248,7 +249,15 @@ PREDEFINED = {schemas.TemplatePredefinedKeys.count_sessions: dashboard.get_proce schemas.TemplatePredefinedKeys.avg_time_to_render: dashboard.get_time_to_render, schemas.TemplatePredefinedKeys.avg_used_js_heap_size: dashboard.get_memory_consumption, schemas.TemplatePredefinedKeys.avg_cpu: dashboard.get_avg_cpu, - schemas.TemplatePredefinedKeys.avg_fps: dashboard.get_avg_fps} + schemas.TemplatePredefinedKeys.avg_fps: dashboard.get_avg_fps, + schemas.TemplatePredefinedKeys.impacted_sessions_by_js_errors: dashboard.get_impacted_sessions_by_js_errors, + schemas.TemplatePredefinedKeys.domains_errors_4xx: dashboard.get_domains_errors_4xx, + schemas.TemplatePredefinedKeys.domains_errors_5xx: dashboard.get_domains_errors_5xx, + schemas.TemplatePredefinedKeys.errors_per_domains: dashboard.get_errors_per_domains, + schemas.TemplatePredefinedKeys.calls_errors: dashboard.get_calls_errors, + schemas.TemplatePredefinedKeys.errors_by_type: dashboard.get_errors_per_type, + schemas.TemplatePredefinedKeys.errors_by_origin: dashboard.get_resources_by_party, + } def get_predefined_metric(key: schemas.TemplatePredefinedKeys, project_id: int, data: dict): @@ -260,7 +269,6 @@ def make_chart_metrics(project_id, user_id, metric_id, data: schemas.CustomMetri include_dashboard=False) if raw_metric is None: return None - print(raw_metric) metric = schemas.CustomMetricAndTemplate = schemas.CustomMetricAndTemplate.parse_obj(raw_metric) if metric.is_template: return get_predefined_metric(key=metric.predefined_key, project_id=project_id, data=data.dict()) diff --git a/api/schemas.py b/api/schemas.py index e518b9914..aa9adc4fa 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -934,6 +934,22 @@ class TemplatePredefinedKeys(str, Enum): avg_used_js_heap_size = "avg_used_js_heap_size" avg_cpu = "avg_cpu" avg_fps = "avg_fps" + impacted_sessions_by_js_errors = "impacted_sessions_by_js_errors" + domains_errors_4xx = "domains_errors_4xx" + domains_errors_5xx = "domains_errors_5xx" + errors_per_domains = "errors_per_domains" + calls_errors = "calls_errors" + errors_by_type = "errors_per_type" + errors_by_origin = "resources_by_party" + + +class TemplatePredefinedUnits(str, Enum): + millisecond = "ms" + minute = "min" + memory = "mb" + frame = "f/s" + percentage = "%" + count = "count" class CustomMetricAndTemplate(BaseModel): diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 1ba9cc0af..14a31349a 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -77,7 +77,14 @@ VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, tr ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview') + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), + ('sessions affected by js errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), + ('top domains with 4xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), + ('top domains with 5xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), + ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), + ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), + ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), + ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 6a0258af7..53b51972e 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1276,7 +1276,14 @@ VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, tr ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview') + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), + ('sessions affected by js errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), + ('top domains with 4xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), + ('top domains with 5xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), + ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), + ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), + ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), + ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 5afd50a77..8d28f814f 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -76,7 +76,14 @@ VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, tr ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview') + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), + ('sessions affected by js errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), + ('top domains with 4xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), + ('top domains with 5xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), + ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), + ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), + ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), + ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 3278a48b7..f955833c9 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1067,7 +1067,14 @@ VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, tr ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview') + ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), + ('sessions affected by js errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), + ('top domains with 4xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), + ('top domains with 5xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), + ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), + ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), + ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), + ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, From eb842085ea5831645b334c790e787c6fb5edeffe Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 16:37:27 +0200 Subject: [PATCH 77/87] feat(api): changed startDate/endDate to startTimestamp/endTimestamp for custom metrics --- api/chalicelib/core/custom_metrics.py | 12 ++++++------ api/schemas.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index a43de6903..15c2ffc49 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -12,8 +12,8 @@ PIE_CHART_GROUP = 5 def __try_live(project_id, data: schemas.TryCustomMetricsPayloadSchema): results = [] for i, s in enumerate(data.series): - s.filter.startDate = data.startDate - s.filter.endDate = data.endDate + s.filter.startDate = data.startTimestamp + s.filter.endDate = data.endTimestamp results.append(sessions.search2_series(data=s.filter, project_id=project_id, density=data.density, view_type=data.view_type, metric_type=data.metric_type, metric_of=data.metric_of, metric_value=data.metric_value)) @@ -93,8 +93,8 @@ def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessi return None results = [] for s in metric.series: - s.filter.startDate = data.startDate - s.filter.endDate = data.endDate + s.filter.startDate = data.startTimestamp + s.filter.endDate = data.endTimestamp results.append({"seriesId": s.series_id, "seriesName": s.name, **sessions.search2_pg(data=s.filter, project_id=project_id, user_id=user_id)}) @@ -311,9 +311,9 @@ def get(metric_id, project_id, user_id, flatten=True): def get_with_template(metric_id, project_id, user_id, include_dashboard=True): with pg_client.PostgresClient() as cur: - sub_query="" + sub_query = "" if include_dashboard: - sub_query="""LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards + sub_query = """LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(connected_dashboards.* ORDER BY is_public,name),'[]'::jsonb) AS dashboards FROM (SELECT dashboard_id, name, is_public FROM dashboards WHERE deleted_at ISNULL diff --git a/api/schemas.py b/api/schemas.py index aa9adc4fa..a5bc349a5 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -804,8 +804,8 @@ class TimeseriesMetricOfType(str, Enum): class CustomMetricSessionsPayloadSchema(FlatSessionsSearch): - startDate: int = Field(TimeUTC.now(-7)) - endDate: int = Field(TimeUTC.now()) + startTimestamp: int = Field(TimeUTC.now(-7)) + endTimestamp: int = Field(TimeUTC.now()) class Config: alias_generator = attribute_to_camel_case From badae1249b48b1267dd142c46b64721f80147e53 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 17:06:57 +0200 Subject: [PATCH 78/87] feat(api): dashboard support for Performance predefined templates --- api/chalicelib/core/dashboards2.py | 13 +++++++++++++ api/schemas.py | 13 +++++++++++++ .../helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 15 ++++++++++++++- .../helm/db/init_dbs/postgresql/init_schema.sql | 15 ++++++++++++++- .../helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 15 ++++++++++++++- .../helm/db/init_dbs/postgresql/init_schema.sql | 15 ++++++++++++++- 6 files changed, 82 insertions(+), 4 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 594db2fd0..fdbd9c7c1 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -257,6 +257,19 @@ PREDEFINED = {schemas.TemplatePredefinedKeys.count_sessions: dashboard.get_proce schemas.TemplatePredefinedKeys.calls_errors: dashboard.get_calls_errors, schemas.TemplatePredefinedKeys.errors_by_type: dashboard.get_errors_per_type, schemas.TemplatePredefinedKeys.errors_by_origin: dashboard.get_resources_by_party, + schemas.TemplatePredefinedKeys.speed_index_by_location: dashboard.get_speed_index_location, + schemas.TemplatePredefinedKeys.slowest_domains: dashboard.get_slowest_domains, + schemas.TemplatePredefinedKeys.sessions_per_browser: dashboard.get_sessions_per_browser, + schemas.TemplatePredefinedKeys.time_to_render: dashboard.get_time_to_render, + schemas.TemplatePredefinedKeys.impacted_sessions_by_slow_pages: dashboard.get_impacted_sessions_by_slow_pages, + schemas.TemplatePredefinedKeys.memory_consumption: dashboard.get_memory_consumption, + schemas.TemplatePredefinedKeys.cpu_load: dashboard.get_avg_cpu, + schemas.TemplatePredefinedKeys.frame_rate: dashboard.get_avg_fps, + schemas.TemplatePredefinedKeys.crashes: dashboard.get_crashes, + schemas.TemplatePredefinedKeys.resources_vs_visually_complete: dashboard.get_resources_vs_visually_complete, + schemas.TemplatePredefinedKeys.pages_dom_buildtime: dashboard.get_pages_dom_build_time, + schemas.TemplatePredefinedKeys.pages_response_time: dashboard.get_pages_response_time, + schemas.TemplatePredefinedKeys.pages_response_time_distribution: dashboard.get_pages_response_time_distribution, } diff --git a/api/schemas.py b/api/schemas.py index a5bc349a5..35f5fc9a0 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -941,6 +941,19 @@ class TemplatePredefinedKeys(str, Enum): calls_errors = "calls_errors" errors_by_type = "errors_per_type" errors_by_origin = "resources_by_party" + speed_index_by_location="speed_location" + slowest_domains="slowest_domains" + sessions_per_browser="sessions_per_browser" + time_to_render="time_to_render" + impacted_sessions_by_slow_pages="impacted_sessions_by_slow_pages" + memory_consumption="memory_consumption" + cpu_load="cpu" + frame_rate="fps" + crashes="crashes" + resources_vs_visually_complete="resources_vs_visually_complete" + pages_dom_buildtime="pages_dom_buildtime" + pages_response_time="pages_response_time" + pages_response_time_distribution="pages_response_time_distribution" class TemplatePredefinedUnits(str, Enum): diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 14a31349a..4b9d91892 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -84,7 +84,20 @@ VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, tr ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), - ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart') + ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), + ('speed index by location', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), + ('slowest domains', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), + ('sessions per browser', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), + ('time to render', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), + ('sessions impacted by slow pages', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), + ('memory consumption', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), + ('cpu load', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), + ('frame rate', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), + ('crashes', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), + ('resources vs visually complete', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), + ('dom build time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), + ('pages response time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), + ('pages response time distribution', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 53b51972e..bcc1d005c 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1283,7 +1283,20 @@ VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, tr ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), - ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart') + ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), + ('speed index by location', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), + ('slowest domains', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), + ('sessions per browser', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), + ('time to render', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), + ('sessions impacted by slow pages', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), + ('memory consumption', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), + ('cpu load', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), + ('frame rate', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), + ('crashes', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), + ('resources vs visually complete', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), + ('dom build time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), + ('pages response time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), + ('pages response time distribution', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 8d28f814f..781fb3be5 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -83,7 +83,20 @@ VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, tr ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), - ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart') + ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), + ('speed index by location', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), + ('slowest domains', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), + ('sessions per browser', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), + ('time to render', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), + ('sessions impacted by slow pages', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), + ('memory consumption', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), + ('cpu load', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), + ('frame rate', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), + ('crashes', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), + ('resources vs visually complete', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), + ('dom build time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), + ('pages response time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), + ('pages response time distribution', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index f955833c9..0737fb8b1 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1074,7 +1074,20 @@ VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, tr ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), - ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart') + ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), + ('speed index by location', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), + ('slowest domains', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), + ('sessions per browser', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), + ('time to render', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), + ('sessions impacted by slow pages', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), + ('memory consumption', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), + ('cpu load', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), + ('frame rate', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), + ('crashes', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), + ('resources vs visually complete', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), + ('dom build time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), + ('pages response time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), + ('pages response time distribution', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, From c26b77ca897831bd808a251f407e36f07d19bf1c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 17:59:54 +0200 Subject: [PATCH 79/87] feat(api): dashboard support for Resources predefined templates --- api/chalicelib/core/dashboards2.py | 5 ++ api/schemas.py | 33 ++++--- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 89 ++++++++++--------- .../db/init_dbs/postgresql/init_schema.sql | 89 ++++++++++--------- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 89 ++++++++++--------- .../db/init_dbs/postgresql/init_schema.sql | 89 ++++++++++--------- 6 files changed, 220 insertions(+), 174 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index fdbd9c7c1..d4053e82b 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -270,6 +270,11 @@ PREDEFINED = {schemas.TemplatePredefinedKeys.count_sessions: dashboard.get_proce schemas.TemplatePredefinedKeys.pages_dom_buildtime: dashboard.get_pages_dom_build_time, schemas.TemplatePredefinedKeys.pages_response_time: dashboard.get_pages_response_time, schemas.TemplatePredefinedKeys.pages_response_time_distribution: dashboard.get_pages_response_time_distribution, + schemas.TemplatePredefinedKeys.missing_resources: dashboard.get_missing_resources_trend, + schemas.TemplatePredefinedKeys.slowest_resources: dashboard.get_slowest_resources, + schemas.TemplatePredefinedKeys.resources_fetch_time: dashboard.get_resources_loading_time, + schemas.TemplatePredefinedKeys.resource_type_vs_response_end: dashboard.resource_type_vs_response_end, + schemas.TemplatePredefinedKeys.resources_count_by_type: dashboard.get_resources_count_by_type, } diff --git a/api/schemas.py b/api/schemas.py index 35f5fc9a0..e97135f0c 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -927,7 +927,7 @@ class TemplatePredefinedKeys(str, Enum): avg_response_time = "avg_response_time" avg_first_paint = "avg_first_paint" avg_dom_content_loaded = "avg_dom_content_loaded" - avg_till_first_bit = "avg_till_first_bit" + avg_till_first_bit = "avg_till_first_byte" avg_time_to_interactive = "avg_time_to_interactive" count_requests = "count_requests" avg_time_to_render = "avg_time_to_render" @@ -941,19 +941,24 @@ class TemplatePredefinedKeys(str, Enum): calls_errors = "calls_errors" errors_by_type = "errors_per_type" errors_by_origin = "resources_by_party" - speed_index_by_location="speed_location" - slowest_domains="slowest_domains" - sessions_per_browser="sessions_per_browser" - time_to_render="time_to_render" - impacted_sessions_by_slow_pages="impacted_sessions_by_slow_pages" - memory_consumption="memory_consumption" - cpu_load="cpu" - frame_rate="fps" - crashes="crashes" - resources_vs_visually_complete="resources_vs_visually_complete" - pages_dom_buildtime="pages_dom_buildtime" - pages_response_time="pages_response_time" - pages_response_time_distribution="pages_response_time_distribution" + speed_index_by_location = "speed_location" + slowest_domains = "slowest_domains" + sessions_per_browser = "sessions_per_browser" + time_to_render = "time_to_render" + impacted_sessions_by_slow_pages = "impacted_sessions_by_slow_pages" + memory_consumption = "memory_consumption" + cpu_load = "cpu" + frame_rate = "fps" + crashes = "crashes" + resources_vs_visually_complete = "resources_vs_visually_complete" + pages_dom_buildtime = "pages_dom_buildtime" + pages_response_time = "pages_response_time" + pages_response_time_distribution = "pages_response_time_distribution" + missing_resources = "missing_resources" + slowest_resources = "slowest_resources" + resources_fetch_time = "resources_loading_time" + resource_type_vs_response_end = "resource_type_vs_response_end" + resources_count_by_type = "resources_count_by_type" class TemplatePredefinedUnits(str, Enum): diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 4b9d91892..113069f24 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -30,7 +30,7 @@ ALTER TABLE IF EXISTS metrics ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS predefined_key text NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, + ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{"col":2,"row":2,"position":0}'::jsonb, ALTER COLUMN project_id DROP NOT NULL, ADD CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), @@ -59,45 +59,54 @@ ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'predefined'; INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'predefined', 'overview'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), - ('sessions affected by js errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), - ('top domains with 4xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), - ('top domains with 5xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), - ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), - ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), - ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), - ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), - ('speed index by location', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), - ('slowest domains', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), - ('sessions per browser', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), - ('time to render', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), - ('sessions impacted by slow pages', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), - ('memory consumption', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), - ('cpu load', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), - ('frame rate', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), - ('crashes', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), - ('resources vs visually complete', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), - ('dom build time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), - ('pages response time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), - ('pages response time distribution', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart') +VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), + ('Request Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), + ('Page Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), + ('Image Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), + ('DOM Content Load Start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), + ('First Meaningful paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), + ('No. of Visited Pages', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), + ('Session Duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), + ('DOM Build Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), + ('Pages Response Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), + ('Response Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), + ('First Paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), + ('DOM Content Loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), + ('Time Till First byte', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_byte', 'predefined', 'overview'), + ('Time To Interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), + ('Captured requests', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), + ('Time To Render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), + ('Memory Consumption', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), + ('CPU Load', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), + ('Frame rate', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_fps', 'predefined', 'overview'), + + ('Sessions Affected by JS Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), + ('Top Domains with 4xx Fetch Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), + ('Top Domains with 5xx Fetch Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), + ('Errors per Domain', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), + ('Fetch Calls with Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), + ('Errors by Type', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), + ('Errors by Origin', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), + + ('Speed Index by Location', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), + ('Slowest Domains', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), + ('Sessions per Browser', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), + ('Time To Render', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), + ('Sessions Impacted by Slow Pages', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), + ('Memory Consumption', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), + ('CPU Load', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), + ('Frame Rate', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), + ('Crashes', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), + ('Resources Loaded vs Visually Complete', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), + ('DOM Build Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), + ('Pages Response Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), + ('Pages Response Time Distribution', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), + + ('Missing Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), + ('Slowest Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), + ('Resources Fetch Time', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_loading_time', 'predefined', 'table'), + ('Resource Loaded vs Response End', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resource_type_vs_response_end', 'predefined', 'stackedBarLineChart'), + ('Breakdown of Loaded Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_count_by_type', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index bcc1d005c..c5303db96 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -811,7 +811,7 @@ $$ is_predefined boolean NOT NULL DEFAULT FALSE, is_template boolean NOT NULL DEFAULT FALSE, predefined_key text NULL DEFAULT NULL, - config jsonb NOT NULL DEFAULT '{}'::jsonb, + config jsonb NOT NULL DEFAULT '{"col": 2,"row": 2,"position": 0}'::jsonb, CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), CONSTRAINT unique_key UNIQUE (predefined_key) @@ -1258,45 +1258,54 @@ $$ LANGUAGE plpgsql; INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'predefined', 'overview'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), - ('sessions affected by js errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), - ('top domains with 4xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), - ('top domains with 5xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), - ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), - ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), - ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), - ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), - ('speed index by location', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), - ('slowest domains', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), - ('sessions per browser', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), - ('time to render', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), - ('sessions impacted by slow pages', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), - ('memory consumption', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), - ('cpu load', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), - ('frame rate', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), - ('crashes', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), - ('resources vs visually complete', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), - ('dom build time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), - ('pages response time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), - ('pages response time distribution', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart') +VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), + ('Request Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), + ('Page Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), + ('Image Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), + ('DOM Content Load Start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), + ('First Meaningful paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), + ('No. of Visited Pages', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), + ('Session Duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), + ('DOM Build Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), + ('Pages Response Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), + ('Response Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), + ('First Paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), + ('DOM Content Loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), + ('Time Till First byte', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_byte', 'predefined', 'overview'), + ('Time To Interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), + ('Captured requests', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), + ('Time To Render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), + ('Memory Consumption', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), + ('CPU Load', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), + ('Frame rate', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_fps', 'predefined', 'overview'), + + ('Sessions Affected by JS Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), + ('Top Domains with 4xx Fetch Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), + ('Top Domains with 5xx Fetch Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), + ('Errors per Domain', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), + ('Fetch Calls with Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), + ('Errors by Type', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), + ('Errors by Origin', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), + + ('Speed Index by Location', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), + ('Slowest Domains', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), + ('Sessions per Browser', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), + ('Time To Render', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), + ('Sessions Impacted by Slow Pages', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), + ('Memory Consumption', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), + ('CPU Load', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), + ('Frame Rate', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), + ('Crashes', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), + ('Resources Loaded vs Visually Complete', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), + ('DOM Build Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), + ('Pages Response Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), + ('Pages Response Time Distribution', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), + + ('Missing Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), + ('Slowest Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), + ('Resources Fetch Time', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_loading_time', 'predefined', 'table'), + ('Resource Loaded vs Response End', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resource_type_vs_response_end', 'predefined', 'stackedBarLineChart'), + ('Breakdown of Loaded Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_count_by_type', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 781fb3be5..ba502c493 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -30,7 +30,7 @@ ALTER TABLE IF EXISTS metrics ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS predefined_key text NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, + ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{"col":2,"row":2,"position":0}'::jsonb, ALTER COLUMN project_id DROP NOT NULL, ADD CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), @@ -58,45 +58,54 @@ ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'map'; ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'predefined'; INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'predefined', 'overview'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), - ('sessions affected by js errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), - ('top domains with 4xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), - ('top domains with 5xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), - ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), - ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), - ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), - ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), - ('speed index by location', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), - ('slowest domains', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), - ('sessions per browser', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), - ('time to render', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), - ('sessions impacted by slow pages', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), - ('memory consumption', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), - ('cpu load', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), - ('frame rate', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), - ('crashes', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), - ('resources vs visually complete', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), - ('dom build time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), - ('pages response time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), - ('pages response time distribution', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart') +VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), + ('Request Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), + ('Page Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), + ('Image Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), + ('DOM Content Load Start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), + ('First Meaningful paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), + ('No. of Visited Pages', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), + ('Session Duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), + ('DOM Build Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), + ('Pages Response Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), + ('Response Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), + ('First Paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), + ('DOM Content Loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), + ('Time Till First byte', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_byte', 'predefined', 'overview'), + ('Time To Interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), + ('Captured requests', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), + ('Time To Render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), + ('Memory Consumption', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), + ('CPU Load', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), + ('Frame rate', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_fps', 'predefined', 'overview'), + + ('Sessions Affected by JS Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), + ('Top Domains with 4xx Fetch Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), + ('Top Domains with 5xx Fetch Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), + ('Errors per Domain', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), + ('Fetch Calls with Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), + ('Errors by Type', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), + ('Errors by Origin', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), + + ('Speed Index by Location', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), + ('Slowest Domains', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), + ('Sessions per Browser', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), + ('Time To Render', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), + ('Sessions Impacted by Slow Pages', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), + ('Memory Consumption', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), + ('CPU Load', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), + ('Frame Rate', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), + ('Crashes', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), + ('Resources Loaded vs Visually Complete', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), + ('DOM Build Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), + ('Pages Response Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), + ('Pages Response Time Distribution', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), + + ('Missing Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), + ('Slowest Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), + ('Resources Fetch Time', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_loading_time', 'predefined', 'table'), + ('Resource Loaded vs Response End', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resource_type_vs_response_end', 'predefined', 'stackedBarLineChart'), + ('Breakdown of Loaded Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_count_by_type', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 0737fb8b1..c8a1e41fb 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -958,7 +958,7 @@ $$ is_predefined boolean NOT NULL DEFAULT FALSE, is_template boolean NOT NULL DEFAULT FALSE, predefined_key text NULL DEFAULT NULL, - config jsonb NOT NULL DEFAULT '{}'::jsonb, + config jsonb NOT NULL DEFAULT '{"col": 2,"row": 2,"position": 0}'::jsonb, CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), CONSTRAINT unique_key UNIQUE (predefined_key) @@ -1049,45 +1049,54 @@ $$ LANGUAGE plpgsql; INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) -VALUES ('sessions count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), - ('avg request load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), - ('avg page load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), - ('avg image load time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), - ('avg dom content load start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), - ('avg first contentful pixel', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), - ('avg visited pages count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), - ('avg session duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), - ('avg pages dom build time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), - ('avg pages response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), - ('avg response time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), - ('avg first paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), - ('avg dom content loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), - ('avg time till first bit', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_bit', 'predefined', 'overview'), - ('avg time to interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), - ('requests count', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), - ('avg time to render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), - ('avg used js heap size', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), - ('avg cpu', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), - ('sessions affected by js errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), - ('top domains with 4xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), - ('top domains with 5xx fetch errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), - ('errors per domain', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), - ('fetch calls with errors', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), - ('errors by type', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), - ('errors by origin', 'errors', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), - ('speed index by location', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), - ('slowest domains', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), - ('sessions per browser', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), - ('time to render', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), - ('sessions impacted by slow pages', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), - ('memory consumption', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), - ('cpu load', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), - ('frame rate', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), - ('crashes', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), - ('resources vs visually complete', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), - ('dom build time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), - ('pages response time', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), - ('pages response time distribution', 'performance', '{"col":1,"row":1,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart') +VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), + ('Request Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), + ('Page Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), + ('Image Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_image_load_time', 'predefined', 'overview'), + ('DOM Content Load Start', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_load_start', 'predefined', 'overview'), + ('First Meaningful paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_contentful_pixel', 'predefined', 'overview'), + ('No. of Visited Pages', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_visited_pages', 'predefined', 'overview'), + ('Session Duration', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_session_duration', 'predefined', 'overview'), + ('DOM Build Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_dom_buildtime', 'predefined', 'overview'), + ('Pages Response Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_pages_response_time', 'predefined', 'overview'), + ('Response Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_response_time', 'predefined', 'overview'), + ('First Paint', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_first_paint', 'predefined', 'overview'), + ('DOM Content Loaded', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_dom_content_loaded', 'predefined', 'overview'), + ('Time Till First byte', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_till_first_byte', 'predefined', 'overview'), + ('Time To Interactive', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_interactive', 'predefined', 'overview'), + ('Captured requests', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_requests', 'predefined', 'overview'), + ('Time To Render', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_time_to_render', 'predefined', 'overview'), + ('Memory Consumption', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_used_js_heap_size', 'predefined', 'overview'), + ('CPU Load', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_cpu', 'predefined', 'overview'), + ('Frame rate', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_fps', 'predefined', 'overview'), + + ('Sessions Affected by JS Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'impacted_sessions_by_js_errors', 'predefined', 'barChart'), + ('Top Domains with 4xx Fetch Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'domains_errors_4xx', 'predefined', 'lineChart'), + ('Top Domains with 5xx Fetch Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'domains_errors_5xx', 'predefined', 'lineChart'), + ('Errors per Domain', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'errors_per_domains', 'predefined', 'table'), + ('Fetch Calls with Errors', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'calls_errors', 'predefined', 'table'), + ('Errors by Type', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'errors_per_type', 'predefined', 'barChart'), + ('Errors by Origin', 'errors', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_by_party', 'predefined', 'stackedBarChart'), + + ('Speed Index by Location', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'speed_location', 'predefined', 'map'), + ('Slowest Domains', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_domains', 'predefined', 'table'), + ('Sessions per Browser', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'sessions_per_browser', 'predefined', 'table'), + ('Time To Render', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'time_to_render', 'predefined', 'areaChart'), + ('Sessions Impacted by Slow Pages', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'impacted_sessions_by_slow_pages', 'predefined', 'areaChart'), + ('Memory Consumption', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'memory_consumption', 'predefined', 'areaChart'), + ('CPU Load', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'cpu', 'predefined', 'areaChart'), + ('Frame Rate', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'fps', 'predefined', 'areaChart'), + ('Crashes', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'crashes', 'predefined', 'areaChart'), + ('Resources Loaded vs Visually Complete', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_vs_visually_complete', 'predefined', 'areaChart'), + ('DOM Build Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_dom_buildtime', 'predefined', 'areaChart'), + ('Pages Response Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), + ('Pages Response Time Distribution', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), + + ('Missing Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), + ('Slowest Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), + ('Resources Fetch Time', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_loading_time', 'predefined', 'table'), + ('Resource Loaded vs Response End', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resource_type_vs_response_end', 'predefined', 'stackedBarLineChart'), + ('Breakdown of Loaded Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_count_by_type', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, From 577185c304c81e87dca55d15512f2d0ad853b163 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 7 Apr 2022 18:24:08 +0200 Subject: [PATCH 80/87] feat(api): dashboard separated config and default config --- api/chalicelib/core/dashboards2.py | 2 +- api/schemas.py | 2 +- ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 10 +++++----- ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql | 4 ++-- scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 10 +++++----- scripts/helm/db/init_dbs/postgresql/init_schema.sql | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index d4053e82b..651500aaa 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -18,7 +18,7 @@ CATEGORY_DESCRIPTION = { def get_templates(project_id, user_id): with pg_client.PostgresClient() as cur: pg_query = cur.mogrify(f"""SELECT category, jsonb_agg(metrics ORDER BY name) AS widgets - FROM (SELECT * + FROM (SELECT * , default_config AS config FROM metrics LEFT JOIN LATERAL (SELECT COALESCE(jsonb_agg(metric_series.* ORDER BY index), '[]'::jsonb) AS series FROM metric_series WHERE metric_series.metric_id = metrics.metric_id diff --git a/api/schemas.py b/api/schemas.py index e97135f0c..7ba9c0413 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -899,7 +899,7 @@ class EditDashboardSchema(CreateDashboardSchema): class UpdateWidgetPayloadSchema(BaseModel): # if you change the config attribute name, please make sure to update it in dashboard2.py - config: dict = Field(default={"col": 1, "row": 1, "position": 0}) + config: dict = Field(default={"col": 2, "row": 2, "position": 0}) class Config: alias_generator = attribute_to_camel_case diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index 113069f24..f1d3cdc9a 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -24,13 +24,13 @@ ALTER TABLE IF EXISTS metrics DROP CONSTRAINT IF EXISTS unique_key; ALTER TABLE IF EXISTS metrics - ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', + ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS predefined_key text NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{"col":2,"row":2,"position":0}'::jsonb, + ADD COLUMN IF NOT EXISTS predefined_key text NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS default_config jsonb NOT NULL DEFAULT '{"col": 2,"row": 2,"position": 0}'::jsonb, ALTER COLUMN project_id DROP NOT NULL, ADD CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), @@ -58,7 +58,7 @@ ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'map'; ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'predefined'; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) +INSERT INTO metrics (name, category, default_config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), ('Request Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), ('Page Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index c5303db96..f2cb95b76 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -811,7 +811,7 @@ $$ is_predefined boolean NOT NULL DEFAULT FALSE, is_template boolean NOT NULL DEFAULT FALSE, predefined_key text NULL DEFAULT NULL, - config jsonb NOT NULL DEFAULT '{"col": 2,"row": 2,"position": 0}'::jsonb, + default_config jsonb NOT NULL DEFAULT '{"col": 2,"row": 2,"position": 0}'::jsonb, CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), CONSTRAINT unique_key UNIQUE (predefined_key) @@ -1257,7 +1257,7 @@ $$ $$ LANGUAGE plpgsql; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) +INSERT INTO metrics (name, category, default_config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), ('Request Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), ('Page Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index ba502c493..c97cd76ac 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -24,13 +24,13 @@ ALTER TABLE IF EXISTS metrics DROP CONSTRAINT IF EXISTS unique_key; ALTER TABLE IF EXISTS metrics - ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS edited_at timestamp NULL DEFAULT NULL, ADD COLUMN IF NOT EXISTS is_pinned boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', + ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS predefined_key text NULL DEFAULT NULL, - ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{"col":2,"row":2,"position":0}'::jsonb, + ADD COLUMN IF NOT EXISTS predefined_key text NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS default_config jsonb NOT NULL DEFAULT '{"col": 2,"row": 2,"position": 0}'::jsonb, ALTER COLUMN project_id DROP NOT NULL, ADD CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), @@ -57,7 +57,7 @@ ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'overview'; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'map'; ALTER TYPE metric_type ADD VALUE IF NOT EXISTS 'predefined'; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) +INSERT INTO metrics (name, category, default_config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), ('Request Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), ('Page Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index c8a1e41fb..3d5239153 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -958,7 +958,7 @@ $$ is_predefined boolean NOT NULL DEFAULT FALSE, is_template boolean NOT NULL DEFAULT FALSE, predefined_key text NULL DEFAULT NULL, - config jsonb NOT NULL DEFAULT '{"col": 2,"row": 2,"position": 0}'::jsonb, + default_config jsonb NOT NULL DEFAULT '{"col": 2,"row": 2,"position": 0}'::jsonb, CONSTRAINT null_project_id_for_template_only CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ), CONSTRAINT unique_key UNIQUE (predefined_key) @@ -1048,7 +1048,7 @@ $$ $$ LANGUAGE plpgsql; -INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) +INSERT INTO metrics (name, category, default_config, is_predefined, is_template, is_public, predefined_key, metric_type, view_type) VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'count_sessions', 'predefined', 'overview'), ('Request Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_request_load_time', 'predefined', 'overview'), ('Page Load Time', 'overview', '{"col":1,"row":1,"position":0}', true, true, true, 'avg_page_load_time', 'predefined', 'overview'), From 351d1749e9b6c64e1ddea199371125ece61dc2e2 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 8 Apr 2022 16:43:55 +0200 Subject: [PATCH 81/87] feat(ui) - dashboard - wip --- frontend/app/api_client.js | 6 +- .../Alerts/AlertFormModal/AlertFormModal.tsx | 2 +- frontend/app/components/Alerts/Alerts.js | 2 +- .../Integrations/SlackAddForm/SlackAddForm.js | 2 +- .../CustomMetriLineChart.tsx | 2 +- .../CustomMetricOverviewChart.tsx | 84 ++++++++++ .../CustomMetricOverviewChart/index.ts | 1 + .../CustomMetricWidget/CustomMetricWidget.tsx | 44 ++--- .../CustomMetricWidgetPreview.tsx | 40 ++--- .../Dashboard/Widgets/ErrorsPerDomain/Bar.css | 2 +- .../Dashboard/Widgets/ErrorsPerDomain/Bar.js | 2 +- .../PredefinedWidgets/CPULoad/CPULoad.tsx | 56 +++++++ .../PredefinedWidgets/CPULoad/index.ts | 1 + .../CallsErrors4xx/CallsErrors4xx.tsx | 49 ++++++ .../PredefinedWidgets/CallsErrors4xx/index.ts | 1 + .../CallsErrors5xx/CallsErrors5xx.tsx | 49 ++++++ .../PredefinedWidgets/CallsErrors5xx/index.ts | 1 + .../PredefinedWidgets/Crashes/Crashes.tsx | 55 +++++++ .../PredefinedWidgets/Crashes/index.ts | 1 + .../DomBuildingTime/DomBuildingTime.tsx | 91 +++++++++++ .../DomBuildingTime/index.ts | 1 + .../ErrorsByOrigin/ErrorsByOrigin.tsx | 48 ++++++ .../PredefinedWidgets/ErrorsByOrigin/index.ts | 1 + .../ErrorsByType/ErrorsByType.tsx | 50 ++++++ .../PredefinedWidgets/ErrorsByType/index.ts | 1 + .../ErrorsPerDomain/ErrorsPerDomain.tsx | 36 +++++ .../ErrorsPerDomain/index.ts | 1 + .../Widgets/PredefinedWidgets/FPS/FPS.tsx | 60 +++++++ .../Widgets/PredefinedWidgets/FPS/index.ts | 1 + .../MemoryConsumption/MemoryConsumption.tsx | 61 +++++++ .../MemoryConsumption/index.ts | 1 + .../ResponseTime/ResponseTime.tsx | 91 +++++++++++ .../PredefinedWidgets/ResponseTime/index.ts | 1 + .../SessionsAffectedByJSErrors.tsx | 47 ++++++ .../SessionsAffectedByJSErrors/index.ts | 1 + .../SlowestDomains/SlowestDomains.tsx | 34 ++++ .../PredefinedWidgets/SlowestDomains/index.ts | 1 + .../TimeToRender/TimeToRender.tsx | 91 +++++++++++ .../PredefinedWidgets/TimeToRender/index.ts | 1 + .../DashboardMetricSelection.tsx | 16 +- .../DashboardSideMenu/DashboardSideMenu.tsx | 12 +- .../DashboardView/DashboardView.tsx | 24 ++- .../DashboardWidgetGrid.tsx | 3 +- .../MetricsSearch/MetricsSearch.tsx | 2 +- .../components/MetricsView/MetricsView.tsx | 6 +- .../components/WidgetChart/WidgetChart.tsx | 82 ++++------ .../components/WidgetForm/WidgetForm.tsx | 22 +-- .../WidgetPredefinedChart.tsx | 81 ++++++++++ .../components/WidgetPredefinedChart/index.ts | 1 + .../WidgetPreview/WidgetPreview.tsx | 2 +- .../WidgetWrapper/WidgetWrapper.tsx | 18 ++- .../SaveSearchModal/SaveSearchModal.tsx | 2 +- .../SavedSearchDropdown.tsx | 2 +- .../app/components/ui/ItemMenu/ItemMenu.js | 3 +- frontend/app/mstore/dashboardStore.ts | 151 +++++++++--------- frontend/app/mstore/metricStore.ts | 16 +- frontend/app/mstore/types/dashboard.ts | 21 ++- frontend/app/mstore/types/widget.ts | 33 +++- frontend/app/services/DashboardService.ts | 23 ++- frontend/app/services/MetricService.ts | 24 +-- 60 files changed, 1313 insertions(+), 250 deletions(-) create mode 100644 frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType/ErrorsByType.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/ErrorsPerDomain.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/FPS/FPS.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/FPS/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption/MemoryConsumption.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/index.ts create mode 100644 frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx create mode 100644 frontend/app/components/Dashboard/components/WidgetPredefinedChart/index.ts diff --git a/frontend/app/api_client.js b/frontend/app/api_client.js index 8fc753c08..626f033ea 100644 --- a/frontend/app/api_client.js +++ b/frontend/app/api_client.js @@ -69,12 +69,16 @@ export default class APIClient { this.siteId = siteId; } - fetch(path, params, options = { clean: true }) { + fetch(path, params, options = { clean: true }) { if (params !== undefined) { const cleanedParams = options.clean ? clean(params) : params; this.init.body = JSON.stringify(cleanedParams); } + if (this.init.method === 'GET') { + delete this.init.body; + } + let fetch = window.fetch; diff --git a/frontend/app/components/Alerts/AlertFormModal/AlertFormModal.tsx b/frontend/app/components/Alerts/AlertFormModal/AlertFormModal.tsx index 74a2552f7..d1459eb6b 100644 --- a/frontend/app/components/Alerts/AlertFormModal/AlertFormModal.tsx +++ b/frontend/app/components/Alerts/AlertFormModal/AlertFormModal.tsx @@ -45,7 +45,7 @@ function AlertFormModal(props: Props) { const onDelete = async (instance) => { if (await confirm({ header: 'Confirm', - confirmButton: 'Yes, Delete', + confirmButton: 'Yes, delete', confirmation: `Are you sure you want to permanently delete this alert?` })) { props.remove(instance.alertId).then(() => { diff --git a/frontend/app/components/Alerts/Alerts.js b/frontend/app/components/Alerts/Alerts.js index aaa99b7a2..afe161aee 100644 --- a/frontend/app/components/Alerts/Alerts.js +++ b/frontend/app/components/Alerts/Alerts.js @@ -32,7 +32,7 @@ const Alerts = props => { const onDelete = async (instance) => { if (await confirm({ header: 'Confirm', - confirmButton: 'Yes, Delete', + confirmButton: 'Yes, delete', confirmation: `Are you sure you want to permanently delete this alert?` })) { props.remove(instance.alertId).then(() => { diff --git a/frontend/app/components/Client/Integrations/SlackAddForm/SlackAddForm.js b/frontend/app/components/Client/Integrations/SlackAddForm/SlackAddForm.js index 16586fd1d..4eb16f868 100644 --- a/frontend/app/components/Client/Integrations/SlackAddForm/SlackAddForm.js +++ b/frontend/app/components/Client/Integrations/SlackAddForm/SlackAddForm.js @@ -22,7 +22,7 @@ class SlackAddForm extends React.PureComponent { remove = async (id) => { if (await confirm({ header: 'Confirm', - confirmButton: 'Yes, Delete', + confirmButton: 'Yes, delete', confirmation: `Are you sure you want to permanently delete this channel?` })) { this.props.remove(id); diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx index ffbbc6b88..59417bd49 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx @@ -11,7 +11,7 @@ interface Props { onClick?: (event, index) => void; } function CustomMetriLineChart(props: Props) { - const { data, params, seriesMap, colors, onClick = () => null } = props; + const { data, params, seriesMap = [], colors, onClick = () => null } = props; return ( void; +} +function CustomMetricOverviewChart(props: Props) { + const { data, params, seriesMap, colors, onClick = () => null } = props; + console.log('data', data) + const gradientDef = Styles.gradientDef(); + return ( +
+
+
+ {/*
{ 'test' }
*/} +
+
+ {/* {prefix} */} + {/*
+
+
*/} + +
+
+ + + {gradientDef} + + + + + + +
+ ) +} + +export default CustomMetricOverviewChart + + +const countView = (avg, unit) => { + if (unit === 'mb') { + if (!avg) return 0; + const count = Math.trunc(avg / 1024 / 1024); + return numberWithCommas(count); + } + if (unit === 'min') { + if (!avg) return 0; + const count = Math.trunc(avg); + return numberWithCommas(count > 1000 ? count +'k' : count); + } + return avg ? numberWithCommas(avg): 0; + } \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/index.ts b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/index.ts new file mode 100644 index 000000000..2aa2ad492 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/index.ts @@ -0,0 +1 @@ +export { default } from './CustomMetricOverviewChart'; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx index c5fd2ad3f..32f800e1f 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx @@ -56,29 +56,29 @@ function CustomMetricWidget(props: Props) { const isTable = metric.viewType === 'table'; const isPieChart = metric.viewType === 'pieChart'; - useEffect(() => { - new APIClient()['post'](`/custom_metrics/${metricParams.metricId}/chart`, { ...metricParams, q: metric.name }) - .then(response => response.json()) - .then(({ errors, data }) => { - if (errors) { - console.log('err', errors) - } else { - const namesMap = data - .map(i => Object.keys(i)) - .flat() - .filter(i => i !== 'time' && i !== 'timestamp') - .reduce((unique: any, item: any) => { - if (!unique.includes(item)) { - unique.push(item); - } - return unique; - }, []); + // useEffect(() => { + // new APIClient()['post'](`/custom_metrics/${metricParams.metricId}/chart`, { ...metricParams, q: metric.name }) + // .then(response => response.json()) + // .then(({ errors, data }) => { + // if (errors) { + // console.log('err', errors) + // } else { + // const namesMap = data + // .map(i => Object.keys(i)) + // .flat() + // .filter(i => i !== 'time' && i !== 'timestamp') + // .reduce((unique: any, item: any) => { + // if (!unique.includes(item)) { + // unique.push(item); + // } + // return unique; + // }, []); - setSeriesMap(namesMap); - setData(getChartFormatter(period)(data)); - } - }).finally(() => setLoading(false)); - }, [period]) + // setSeriesMap(namesMap); + // setData(getChartFormatter(period)(data)); + // } + // }).finally(() => setLoading(false)); + // }, [period]) const clickHandlerTable = (filters) => { const activeWidget = { diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx index 10936f1de..73321405c 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx @@ -61,27 +61,27 @@ function CustomMetricWidget(props: Props) { setLoading(true); // fetch new data for the widget preview - new APIClient()['post']('/custom_metrics/try', { ...metricParams, ...metric.toSaveData() }) - .then(response => response.json()) - .then(({ errors, data }) => { - if (errors) { - console.log('err', errors) - } else { - const namesMap = data - .map(i => Object.keys(i)) - .flat() - .filter(i => i !== 'time' && i !== 'timestamp') - .reduce((unique: any, item: any) => { - if (!unique.includes(item)) { - unique.push(item); - } - return unique; - }, []); + // new APIClient()['post']('/custom_metrics/try', { ...metricParams, ...metric.toSaveData() }) + // .then(response => response.json()) + // .then(({ errors, data }) => { + // if (errors) { + // console.log('err', errors) + // } else { + // const namesMap = data + // .map(i => Object.keys(i)) + // .flat() + // .filter(i => i !== 'time' && i !== 'timestamp') + // .reduce((unique: any, item: any) => { + // if (!unique.includes(item)) { + // unique.push(item); + // } + // return unique; + // }, []); - setSeriesMap(namesMap); - setData(getChartFormatter(period)(data)); - } - }).finally(() => setLoading(false)); + // setSeriesMap(namesMap); + // setData(getChartFormatter(period)(data)); + // } + // }).finally(() => setLoading(false)); }, [metric]) const onDateChange = (changedDates) => { diff --git a/frontend/app/components/Dashboard/Widgets/ErrorsPerDomain/Bar.css b/frontend/app/components/Dashboard/Widgets/ErrorsPerDomain/Bar.css index d3d399918..529aa15eb 100644 --- a/frontend/app/components/Dashboard/Widgets/ErrorsPerDomain/Bar.css +++ b/frontend/app/components/Dashboard/Widgets/ErrorsPerDomain/Bar.css @@ -1,5 +1,5 @@ .bar { - height: 10px; + height: 5px; background-color: red; width: 100%; border-radius: 3px; diff --git a/frontend/app/components/Dashboard/Widgets/ErrorsPerDomain/Bar.js b/frontend/app/components/Dashboard/Widgets/ErrorsPerDomain/Bar.js index 99b37a032..8a09c13d4 100644 --- a/frontend/app/components/Dashboard/Widgets/ErrorsPerDomain/Bar.js +++ b/frontend/app/components/Dashboard/Widgets/ErrorsPerDomain/Bar.js @@ -10,7 +10,7 @@ const Bar = ({ className = '', width = 0, avg, domain, color }) => { {`${avg}`}
-
{domain}
+
{domain}
) } diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx new file mode 100644 index 000000000..5e5853251 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function CPULoad(props: Props) { + const { data } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + + return ( + + + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + /> + + + + + + ); +} + +export default CPULoad; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/index.ts new file mode 100644 index 000000000..37cec8b40 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/index.ts @@ -0,0 +1 @@ +export { default } from './CPULoad' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx new file mode 100644 index 000000000..bd54bc5e3 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function CallsErrors4xx(props: Props) { + const { data } = props; + return ( + + + + + + + + + {/* { data.namesMap.map((key, index) => ( + + ))} */} + + + + ); +} + +export default CallsErrors4xx; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/index.ts new file mode 100644 index 000000000..a21e4a950 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/index.ts @@ -0,0 +1 @@ +export { default } from './CallsErrors4xx' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx new file mode 100644 index 000000000..e55bbb0cb --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function CallsErrors5xx(props: Props) { + const { data } = props; + return ( + + + + + + + + + {/* { data.namesMap.map((key, index) => ( + + ))} */} + + + + ); +} + +export default CallsErrors5xx; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/index.ts new file mode 100644 index 000000000..661204c0d --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/index.ts @@ -0,0 +1 @@ +export { default } from './CallsErrors5xx' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx new file mode 100644 index 000000000..a73537d69 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function Crashes(props: Props) { + const { data } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + return ( + + + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + /> + + + + + + ); +} + +export default Crashes; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/index.ts new file mode 100644 index 000000000..ba5ce0764 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/index.ts @@ -0,0 +1 @@ +export { default } from './Crashes' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx new file mode 100644 index 000000000..4bda65afa --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import { withRequest } from 'HOCs' +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; +import WidgetAutoComplete from 'Shared/WidgetAutoComplete'; +import { toUnderscore } from 'App/utils'; + +const WIDGET_KEY = 'pagesDomBuildtime'; + +interface Props { + data: any + optionsLoading: any + fetchOptions: any + options: any +} +function DomBuildingTime(props: Props) { + const { data, optionsLoading } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + + + const onSelect = (params) => { + const _params = { density: 70 } + console.log('params', params) // TODO reload the data with new params; + // this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value }) + } + + return ( + + <> +
+ + +
+ + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + /> + + + + + +
+ ); +} + +export default withRequest({ + dataName: "options", + initialData: [], + dataWrapper: data => data, + loadingName: 'optionsLoading', + requestName: "fetchOptions", + endpoint: '/dashboard/' + toUnderscore(WIDGET_KEY) + '/search', + method: 'GET' +})(DomBuildingTime) \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/index.ts new file mode 100644 index 000000000..a3191aaf7 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/index.ts @@ -0,0 +1 @@ +export { default } from './DomBuildingTime' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx new file mode 100644 index 000000000..2eb5aa1dd --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function ErrorsByOrigin(props: Props) { + const { data } = props; + return ( + + + + + + + + + 1st Party} dataKey="firstParty" stackId="a" fill={Styles.colors[0]} /> + 3rd Party} dataKey="thirdParty" stackId="a" fill={Styles.colors[2]} /> + + + + ); +} + +export default ErrorsByOrigin; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/index.ts new file mode 100644 index 000000000..18a8b9ec3 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/index.ts @@ -0,0 +1 @@ +export { default } from './ErrorsByOrigin' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType/ErrorsByType.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType/ErrorsByType.tsx new file mode 100644 index 000000000..993ff8837 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType/ErrorsByType.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function ErrorsByType(props: Props) { + const { data } = props; + return ( + + + + + + + + + + + + + + + + ); +} + +export default ErrorsByType; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType/index.ts new file mode 100644 index 000000000..f889ccec7 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType/index.ts @@ -0,0 +1 @@ +export { default } from './ErrorsByType' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/ErrorsPerDomain.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/ErrorsPerDomain.tsx new file mode 100644 index 000000000..d51e7539b --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/ErrorsPerDomain.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { numberWithCommas } from 'App/utils'; +import Bar from 'App/components/Dashboard/Widgets/ErrorsPerDomain/Bar'; + +interface Props { + data: any +} +function ErrorsPerDomain(props: Props) { + const { data } = props; + console.log('ErrorsPerDomain', data); + // const firstAvg = 10; + const firstAvg = data.chart[0] && data.chart[0].errorsCount; + return ( + +
+ {data.chart.map((item, i) => + + )} +
+
+ ); +} + +export default ErrorsPerDomain; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/index.ts new file mode 100644 index 000000000..d08e3867b --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/index.ts @@ -0,0 +1 @@ +export { default } from './ErrorsPerDomain' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/FPS/FPS.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/FPS/FPS.tsx new file mode 100644 index 000000000..13f0c17b9 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/FPS/FPS.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function FPS(props: Props) { + const { data } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + + return ( + + <> +
+ +
+ + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + /> + + + + + +
+ ); +} + +export default FPS; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/FPS/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/FPS/index.ts new file mode 100644 index 000000000..85a43ba5e --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/FPS/index.ts @@ -0,0 +1 @@ +export { default } from './FPS' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption/MemoryConsumption.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption/MemoryConsumption.tsx new file mode 100644 index 000000000..121cf598c --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption/MemoryConsumption.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function MemoryConsumption(props: Props) { + const { data } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + + return ( + + <> +
+ +
+ + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "JS Heap Size (mb)" }} + /> + + + + + +
+ ); +} + +export default MemoryConsumption; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption/index.ts new file mode 100644 index 000000000..7d426259c --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption/index.ts @@ -0,0 +1 @@ +export { default } from './MemoryConsumption' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx new file mode 100644 index 000000000..a6c2a025f --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import { withRequest } from 'HOCs' +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; +import WidgetAutoComplete from 'Shared/WidgetAutoComplete'; +import { toUnderscore } from 'App/utils'; + +const WIDGET_KEY = 'pagesResponseTime'; + +interface Props { + data: any + optionsLoading: any + fetchOptions: any + options: any +} +function ResponseTime(props: Props) { + const { data, optionsLoading } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + + + const onSelect = (params) => { + const _params = { density: 70 } + console.log('params', params) // TODO reload the data with new params; + // this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value }) + } + + return ( + + <> +
+ + +
+ + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "Page Response Time (ms)" }} + /> + + + + + +
+ ); +} + +export default withRequest({ + dataName: "options", + initialData: [], + dataWrapper: data => data, + loadingName: 'optionsLoading', + requestName: "fetchOptions", + endpoint: '/dashboard/' + toUnderscore(WIDGET_KEY) + '/search', + method: 'GET' +})(ResponseTime) \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/index.ts new file mode 100644 index 000000000..95effcb83 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/index.ts @@ -0,0 +1 @@ +export { default } from './ResponseTime' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx new file mode 100644 index 000000000..df55d3811 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function SessionsAffectedByJSErrors(props: Props) { + const { data } = props; + return ( + + + + + + + + + + + + + ); +} + +export default SessionsAffectedByJSErrors; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/index.ts new file mode 100644 index 000000000..b160b1af1 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/index.ts @@ -0,0 +1 @@ +export { default } from './SessionsAffectedByJSErrors' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx new file mode 100644 index 000000000..a7334e650 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { numberWithCommas } from 'App/utils'; +import Bar from 'App/components/Dashboard/Widgets/SlowestDomains/Bar'; + +interface Props { + data: any +} +function SlowestDomains(props: Props) { + const { data } = props; + const firstAvg = data.chart[0] && data.chart[0].errorsCount; + return ( + +
+ {data.chart.map((item, i) => + + )} +
+
+ ); +} + +export default SlowestDomains; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/index.ts new file mode 100644 index 000000000..311262347 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/index.ts @@ -0,0 +1 @@ +export { default } from './SlowestDomains' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx new file mode 100644 index 000000000..5332f71c5 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import { withRequest } from 'HOCs' +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; +import WidgetAutoComplete from 'Shared/WidgetAutoComplete'; +import { toUnderscore } from 'App/utils'; + +const WIDGET_KEY = 'timeToRender'; + +interface Props { + data: any + optionsLoading: any + fetchOptions: any + options: any +} +function TimeToRender(props: Props) { + const { data, optionsLoading } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + + + const onSelect = (params) => { + const _params = { density: 70 } + console.log('params', params) // TODO reload the data with new params; + // this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value }) + } + + return ( + + <> +
+ + +
+ + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "Time to Render (ms)" }} + /> + + + + + +
+ ); +} + +export default withRequest({ + dataName: "options", + initialData: [], + dataWrapper: data => data, + loadingName: 'optionsLoading', + requestName: "fetchOptions", + endpoint: '/dashboard/' + toUnderscore(WIDGET_KEY) + '/search', + method: 'GET' +})(TimeToRender) \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/index.ts new file mode 100644 index 000000000..0e806bf6d --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/index.ts @@ -0,0 +1 @@ +export { default } from './TimeToRender' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index 475aeaa6d..8c0930398 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -87,15 +87,19 @@ function DashboardMetricSelection(props) {
-
+
{activeCategory && activeCategory.widgets.map((widget: any) => ( -
dashboardStore.toggleWidgetSelection(widget)} - > - -
+ /> ))}
diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 087ae7e07..3d1901be2 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -1,4 +1,4 @@ -import { useObserver, observer, useLocalObservable } from 'mobx-react-lite'; +import { useObserver } from 'mobx-react-lite'; import React from 'react'; import { SideMenuitem, SideMenuHeader, Icon, Button } from 'UI'; import { useStore } from 'App/mstore'; @@ -7,6 +7,7 @@ import { withSiteId, dashboardSelected, metrics } from 'App/routes'; import { useModal } from 'App/components/Modal'; import DashbaordListModal from '../DashbaordListModal'; import DashboardModal from '../DashboardModal'; +import cn from 'classnames'; const SHOW_COUNT = 5; interface Props { @@ -36,20 +37,25 @@ function DashboardSideMenu(props: Props) { showModal(, {}) } + const togglePinned = (dashboard) => { + dashboardStore.updatePinned(dashboard.dashboardId); + } + return useObserver(() => (
- {dashboardsPicked.map((item: any) => ( + {dashboardsPicked.sort((a: any, b: any) => a.isPinned === b.isPinned ? 0 : a.isPinned ? -1 : 1 ).map((item: any) => ( onItemClick(item)} + className="group" leading = {(
{item.isPublic &&
} - {item.isPinned &&
} + {
togglePinned(item)}>
}
)} /> diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index f3dfdabcc..5d698f704 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -10,6 +10,7 @@ import { withRouter } from 'react-router-dom'; import { useModal } from 'App/components/Modal'; import DashboardModal from '../DashboardModal'; import DashboardEditModal from '../DashboardEditModal'; +import DateRange from 'Shared/DateRange'; interface Props { siteId: number; @@ -23,6 +24,7 @@ function DashboardView(props: Props) { const { hideModal, showModal } = useModal(); const loading = useObserver(() => dashboardStore.fetchingDashboard); const dashboard: any = dashboardStore.selectedDashboard + const period = useObserver(() => dashboardStore.period); const [showEditModal, setShowEditModal] = React.useState(false); useEffect(() => { @@ -42,7 +44,7 @@ function DashboardView(props: Props) { const onDelete = async () => { if (await confirm({ header: 'Confirm', - confirmButton: 'Yes, Delete', + confirmButton: 'Yes, delete', confirmation: `Are you sure you want to permanently delete this Dashboard?` })) { dashboardStore.deleteDashboard(dashboard).then(() => { @@ -53,7 +55,7 @@ function DashboardView(props: Props) { } } - return ( + return useObserver(() => ( setShowEditModal(false)} />
- {/* */}
-
+
+
+ Time Range + dashboardStore.setPeriod(period)} + customRangeRight + direction="left" + /> +
+
- ) + )); } export default withRouter(withModal(observer(DashboardView))); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx index 77356a279..28638f5de 100644 --- a/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx +++ b/frontend/app/components/Dashboard/components/DashboardWidgetGrid/DashboardWidgetGrid.tsx @@ -29,7 +29,7 @@ function DashboardWidgetGrid(props) {
} > -
+
{list && list.map((item, index) => ( dashbaord.swapWidgetPosition(dragIndex, hoverIndex)} dashboardId={dashboardId} siteId={siteId} + isWidget={true} /> ))}
diff --git a/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx index 6941b6ec0..066598c8e 100644 --- a/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx +++ b/frontend/app/components/Dashboard/components/MetricsSearch/MetricsSearch.tsx @@ -19,7 +19,7 @@ function MetricsSearch(props) { return useObserver(() => (
- + metricStore.metrics.length); React.useEffect(() => { metricStore.fetchList(); @@ -19,7 +20,10 @@ function MetricsView(props: Props) { return useObserver(() => (
- +
+ + {metricsCount} +
diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index b20c3a1b4..86bffb9e9 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -1,72 +1,58 @@ import React, { useState, useRef, useEffect } from 'react'; -import Period, { LAST_24_HOURS, LAST_30_MINUTES, YESTERDAY, LAST_7_DAYS } from 'Types/app/period'; -import CustomMetriLineChart from '../../Widgets/CustomMetricsWidgets/CustomMetriLineChart'; -import CustomMetricPercentage from '../../Widgets/CustomMetricsWidgets/CustomMetricPercentage'; -import CustomMetricTable from '../../Widgets/CustomMetricsWidgets/CustomMetricTable'; -import CustomMetricPieChart from '../../Widgets/CustomMetricsWidgets/CustomMetricPieChart'; -import APIClient from 'App/api_client'; -import { Styles } from '../../Widgets/common'; -import { getChartFormatter } from 'Types/dashboard/helper'; -import { observer, useObserver } from 'mobx-react-lite'; +import CustomMetriLineChart from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart'; +import CustomMetricPercentage from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage'; +import CustomMetricTable from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable'; +import CustomMetricPieChart from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPieChart'; +import { Styles } from 'App/components/Dashboard/Widgets/common'; +import { observer, useObserver, useLocalObservable } from 'mobx-react-lite'; import { Loader } from 'UI'; import { useStore } from 'App/mstore'; +import WidgetPredefinedChart from '../WidgetPredefinedChart'; interface Props { metric: any; + isWidget?: boolean } function WidgetChart(props: Props) { - const metric = useObserver(() => props.metric); - const { metricStore } = useStore(); - // const metric: any = useObserver(() => metricStore.instance); - const series = useObserver(() => metric.series); + const { isWidget = false, metric } = props; + // const metric = useObserver(() => props.metric); + const { dashboardStore } = useStore(); + const period = useObserver(() => dashboardStore.period); const colors = Styles.customMetricColors; const [loading, setLoading] = useState(false) - const [data, setData] = useState({ chart: [{}] }) const [seriesMap, setSeriesMap] = useState([]); - const [period, setPeriod] = useState(Period({ rangeName: metric.rangeName, startDate: metric.startDate, endDate: metric.endDate })); const params = { density: 70 } const metricParams = { ...params, metricId: metric.metricId, viewType: 'lineChart' } const prevMetricRef = useRef(); + const [data, setData] = useState(metric.data); useEffect(() => { - // Check for title change if (prevMetricRef.current && prevMetricRef.current.name !== metric.name) { prevMetricRef.current = metric; return }; prevMetricRef.current = metric; - setLoading(true); - // fetch new data for the widget preview - new APIClient()['post']('/custom_metrics/try', { ...metricParams, ...metric.toJson() }) - .then(response => response.json()) - .then(({ errors, data }) => { - if (errors) { - console.log('err', errors) - } else { - const namesMap = data - .map(i => Object.keys(i)) - .flat() - .filter(i => i !== 'time' && i !== 'timestamp') - .reduce((unique: any, item: any) => { - if (!unique.includes(item)) { - unique.push(item); - } - return unique; - }, []); - - setSeriesMap(namesMap); - setData(getChartFormatter(period)(data)); - } - }).finally(() => setLoading(false)); - }, [metric.data]); + setLoading(true); + const data = isWidget ? {} : { ...metricParams, ...metric.toJson() }; + dashboardStore.fetchMetricChartData(metric, data, isWidget).then((res: any) => { + setData(res); + }).finally(() => { + setLoading(false); + }); + }, [period]); const renderChart = () => { - const { metricType, viewType } = metric; + const { metricType, viewType, predefinedKey } = metric; + + if (metricType === 'predefined') { + return + } + if (metricType === 'timeseries') { if (viewType === 'lineChart') { return ( @@ -85,12 +71,12 @@ function WidgetChart(props: Props) { if (metricType === 'table') { if (viewType === 'table') { - return ; + return ; } else if (viewType === 'pieChart') { return ( @@ -100,11 +86,11 @@ function WidgetChart(props: Props) { return
Unknown
; } - return ( + return useObserver(() => ( {renderChart()} - ); + )); } -export default observer(WidgetChart); \ No newline at end of file +export default WidgetChart; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index bc7803c85..6813a07aa 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -70,7 +70,7 @@ function WidgetForm(props: Props) { const onDelete = async () => { if (await confirm({ header: 'Confirm', - confirmButton: 'Yes, Delete', + confirmButton: 'Yes, delete', confirmation: `Are you sure you want to permanently delete this metric?` })) { metricStore.delete(metric).then(props.onDelete); @@ -122,10 +122,10 @@ function WidgetForm(props: Props) { <> issue type )} @@ -134,12 +134,12 @@ function WidgetForm(props: Props) { <> showing )} diff --git a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx new file mode 100644 index 000000000..da6c49280 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { Styles } from 'App/components/Dashboard/Widgets/common'; +import CustomMetricOverviewChart from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart'; +import ErrorsByType from 'App/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByType'; +import ErrorsByOrigin from 'App/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin'; +import ErrorsPerDomain from 'App/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain'; +import { useObserver } from 'mobx-react-lite'; +import SessionsAffectedByJSErrors from '../../Widgets/PredefinedWidgets/SessionsAffectedByJSErrors'; +import CallsErrors4xx from '../../Widgets/PredefinedWidgets/CallsErrors4xx'; +import CallsErrors5xx from '../../Widgets/PredefinedWidgets/CallsErrors5xx'; +import CPULoad from '../../Widgets/PredefinedWidgets/CPULoad'; +import Crashes from '../../Widgets/PredefinedWidgets/Crashes'; +import DomBuildingTime from '../../Widgets/PredefinedWidgets/DomBuildingTime'; +import FPS from '../../Widgets/PredefinedWidgets/FPS'; +import MemoryConsumption from '../../Widgets/PredefinedWidgets/MemoryConsumption'; +import ResponseTime from '../../Widgets/PredefinedWidgets/ResponseTime'; +import TimeToRender from '../../Widgets/PredefinedWidgets/TimeToRender'; +import SlowestDomains from '../../Widgets/PredefinedWidgets/SlowestDomains'; + +interface Props { + data: any; + predefinedKey: string +} +function WidgetPredefinedChart(props: Props) { + const { data, predefinedKey } = props; + // const { viewType } = data; + const params = { density: 70 } + + const renderWidget = () => { + switch (predefinedKey) { + // ERRORS + case 'errors_per_type': + return + case 'errors_per_domains': + return + case 'resources_by_party': + return + case 'impacted_sessions_by_js_errors': + return + case 'domains_errors_4xx': + return + case 'domains_errors_5xx': + return + + // PERFORMANCE + case 'cpu': + return + case 'crashes': + return + case 'pages_dom_buildtime': + return + case 'fps': + return + case 'memory_consumption': + return + case 'pages_response_time': + return + // case 'pages_response_time_distribution': + // case 'resources_vs_visually_complete': + // case 'impacted_sessions_by_slow_pages': + // case 'sessions_per_browser': + case 'slowest_domains': + return + // case 'speed_location': + case 'time_to_render': + return + + + default: + return
No widget found
+ } + } + + return useObserver(() => ( + <> + {renderWidget()} + + )); +} + +export default WidgetPredefinedChart; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/index.ts b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/index.ts new file mode 100644 index 000000000..e54ae37cd --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/index.ts @@ -0,0 +1 @@ +export { default } from './WidgetPredefinedChart' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index cf2e9fe21..4ec8a63e2 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -78,7 +78,7 @@ function WidgetPreview(props: Props) {
- +
)); diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index c667ae171..9c723129e 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -21,10 +21,13 @@ interface Props { siteId?: string, active?: boolean; history?: any + onClick?: () => void; + isWidget?: boolean; } function WidgetWrapper(props: Props) { const { dashboardStore } = useStore(); - const { active = false, widget, index = 0, moveListItem = null, isPreview = false, isTemplate = false, dashboardId, siteId } = props; + const { isWidget = false, active = false, index = 0, moveListItem = null, isPreview = false, isTemplate = false, dashboardId, siteId } = props; + const widget = useObserver(() => props.widget); const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -51,8 +54,8 @@ function WidgetWrapper(props: Props) { const onDelete = async () => { if (await confirm({ header: 'Confirm', - confirmButton: 'Yes, Delete', - confirmation: `Are you sure you want to permanently delete this Dashboard?` + confirmButton: 'Yes, delete', + confirmation: `Are you sure you want to permanently delete the widget from this dashboard?` })) { dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); } @@ -63,7 +66,7 @@ function WidgetWrapper(props: Props) { } const onChartClick = () => { - if (isPreview || isTemplate) return; + if (!isWidget || widget.metricType === 'predefined') return; props.history.push(withSiteId(dashboardMetricDetails(dashboardId, widget.metricId),siteId)); } @@ -80,13 +83,14 @@ function WidgetWrapper(props: Props) { borderColor: (canDrop && isOver) || active ? '#394EFF' : (isPreview ? 'transparent' : '#EEEEEE'), }} ref={dragDropRef} + onClick={props.onClick ? props.onClick : () => {}} >

{widget.name}

- {!isPreview && !isTemplate && ( + {isWidget && (
-
- +
+
diff --git a/frontend/app/components/shared/SaveSearchModal/SaveSearchModal.tsx b/frontend/app/components/shared/SaveSearchModal/SaveSearchModal.tsx index 1ba6b3d56..668b657b0 100644 --- a/frontend/app/components/shared/SaveSearchModal/SaveSearchModal.tsx +++ b/frontend/app/components/shared/SaveSearchModal/SaveSearchModal.tsx @@ -38,7 +38,7 @@ function SaveSearchModal(props: Props) { const onDelete = async () => { if (await confirm({ header: 'Confirm', - confirmButton: 'Yes, Delete', + confirmButton: 'Yes, delete', confirmation: `Are you sure you want to permanently delete this Saved search?`, })) { props.remove(savedSearch.searchId).then(() => { diff --git a/frontend/app/components/shared/SavedSearch/components/SavedSearchDropdown/SavedSearchDropdown.tsx b/frontend/app/components/shared/SavedSearch/components/SavedSearchDropdown/SavedSearchDropdown.tsx index a141dbb5b..61f566680 100644 --- a/frontend/app/components/shared/SavedSearch/components/SavedSearchDropdown/SavedSearchDropdown.tsx +++ b/frontend/app/components/shared/SavedSearch/components/SavedSearchDropdown/SavedSearchDropdown.tsx @@ -42,7 +42,7 @@ function SavedSearchDropdown(props: Props) { const onDelete = async (instance) => { if (await confirm({ header: 'Confirm', - confirmButton: 'Yes, Delete', + confirmButton: 'Yes, delete', confirmation: `Are you sure you want to permanently delete this search?` })) { props.remove(instance.alertId).then(() => { diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.js b/frontend/app/components/ui/ItemMenu/ItemMenu.js index 1938f5a3c..2663a7b2a 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.js +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.js @@ -1,6 +1,7 @@ import { Icon } from 'UI'; import styles from './itemMenu.css'; import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv'; +import cn from 'classnames'; export default class ItemMenu extends React.PureComponent { state = { @@ -29,7 +30,7 @@ export default class ItemMenu extends React.PureComponent { >
{ this.menuBtnRef = ref; } } - className="w-10 h-10 cursor-pointer bg-white rounded-full flex items-center justify-center hover:bg-gray-lightest" + className={cn("w-10 h-10 cursor-pointer rounded-full flex items-center justify-center hover:bg-gray-light", { 'bg-gray-light' : displayed })} onClick={ this.toggleMenu } role="button" > diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 85832699e..6f2e35da2 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -3,12 +3,17 @@ import Dashboard, { IDashboard } from "./types/dashboard" import Widget, { IWidget } from "./types/widget"; import { dashboardService, metricService } from "App/services"; import { toast } from 'react-toastify'; +import Period, { LAST_24_HOURS, LAST_7_DAYS } from 'Types/app/period'; +import { getChartFormatter } from 'Types/dashboard/helper'; export interface IDashboardSotre { dashboards: IDashboard[] selectedDashboard: IDashboard | null dashboardInstance: IDashboard selectedWidgets: IWidget[] + startTimestamp: number + endTimestamp: number + period: Period siteId: any currentWidget: Widget @@ -52,6 +57,10 @@ export interface IDashboardSotre { fetchTemplates(): Promise deleteDashboardWidget(dashboardId: string, widgetId: string): Promise addWidgetToDashboard(dashboard: IDashboard, metricIds: any): Promise + + updatePinned(dashboardId: string): Promise + fetchMetricChartData(metric: IWidget, data: any, isWidget: boolean): Promise + setPeriod(period: any): void } export default class DashboardStore implements IDashboardSotre { siteId: any = null @@ -63,6 +72,9 @@ export default class DashboardStore implements IDashboardSotre { currentWidget: Widget = new Widget() widgetCategories: any[] = [] widgets: Widget[] = [] + period: Period = Period({ rangeName: LAST_7_DAYS }) + startTimestamp: number = 0 + endTimestamp: number = 0 // Metrics metricsPage: number = 1 @@ -78,8 +90,6 @@ export default class DashboardStore implements IDashboardSotre { constructor() { makeAutoObservable(this, { widgetCategories: observable.ref, - // dashboardInstance: observable.ref, - resetCurrentWidget: action, addDashboard: action, removeDashboard: action, @@ -99,32 +109,11 @@ export default class DashboardStore implements IDashboardSotre { removeSelectedWidgetByCategory: action, toggleWidgetSelection: action, fetchTemplates: action, + updatePinned: action, + setPeriod: action, + + fetchMetricChartData: action }) - - - // TODO remove this sample data - - // for (let i = 0; i < 4; i++) { - // const cat: any = { - // name: `Category ${i + 1}`, - // categoryId: i, - // description: `Category ${i + 1} description`, - // widgets: [] - // } - - // const randomNumber = Math.floor(Math.random() * (5 - 2 + 1)) + 2 - // for (let j = 0; j < randomNumber; j++) { - // const widget: any= new Widget(); - // widget.widgetId = `${i}-${j}` - // widget.viewType = 'lineChart' - // widget.name = `Widget ${i}-${j}`; - // // widget.metricType = ['timeseries', 'table'][Math.floor(Math.random() * 2)]; - // widget.metricType = 'timeseries'; - // cat.widgets.push(widget); - // } - - // this.widgetCategories.push(cat) - // } } toggleAllSelectedWidgets(isSelected: boolean) { @@ -180,7 +169,7 @@ export default class DashboardStore implements IDashboardSotre { return dashboardService.getDashboards() .then((list: any) => { runInAction(() => { - this.dashboards = list.map(d => new Dashboard().fromJson(d)) + this.dashboards = list.map(d => new Dashboard().fromJson(d)).sort((a, b) => a.position - b.position) }) }).finally(() => { runInAction(() => { @@ -383,53 +372,65 @@ export default class DashboardStore implements IDashboardSotre { }) } -} -function getRandomWidget() { - const widget = new Widget(); - widget.widgetId = Math.floor(Math.random() * 100); - widget.name = randomMetricName(); - // widget.type = "random"; - widget.colSpan = Math.floor(Math.random() * 2) + 1; - return widget; -} - -function generateRandomPlaceName() { - const placeNames = [ - "New York", - "Los Angeles", - "Chicago", - "Houston", - "Philadelphia", - "Phoenix", - "San Antonio", - "San Diego", - ] - return placeNames[Math.floor(Math.random() * placeNames.length)] -} - - -function randomMetricName () { - const metrics = ["Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders", "Revenue", "Profit", "Expenses", "Sales", "Orders"]; - return metrics[Math.floor(Math.random() * metrics.length)]; -} - -function getRandomDashboard(id: any = null, isPinned = false) { - const dashboard = new Dashboard(); - dashboard.name = generateRandomPlaceName(); - dashboard.dashboardId = id ? id : Math.floor(Math.random() * 10); - dashboard.isPinned = isPinned; - for (let i = 0; i < 8; i++) { - const widget = getRandomWidget(); - widget.position = i; - dashboard.addWidget(widget); + updatePinned(dashboardId: string): Promise { + // this.isSaving = true + return dashboardService.updatePinned(dashboardId).then(() => { + toast.success('Dashboard pinned successfully') + this.dashboards.forEach(d => { + if (d.dashboardId === dashboardId) { + d.isPinned = true + } else { + d.isPinned = false + } + }) + }).catch(() => { + toast.error('Dashboard could not be pinned') + }).finally(() => { + // this.isSaving = false + }) + } + + setPeriod(period: any) { + this.period = Period({ start: period.startDate, end: period.endDate, rangeName: period.rangeValue }) } - return dashboard; -} -const sampleDashboards = [ - getRandomDashboard(1, true), - getRandomDashboard(2), - getRandomDashboard(3), - getRandomDashboard(4), -] \ No newline at end of file + fetchMetricChartData(metric: IWidget, data: any, isWidget: boolean = false): Promise { + const period = this.period.toTimestamps() + return new Promise((resolve, reject) => { + // this.isLoading = true + return metricService.getMetricChartData(metric, { ...period, ...data, key: metric.predefinedKey }, isWidget) + .then(data => { + if (metric.metricType === 'predefined' && metric.viewType === 'overview') { + const _data = { ...data, chart: getChartFormatter(this.period)(data.chart) } + metric.setData(_data) + resolve(_data); + } else { + if (metric.predefinedKey === 'errors_per_domains') { + console.log('errors_per_domains', data) + data.chart = data + } else { + data.chart = getChartFormatter(this.period)(Array.isArray(data) ? data : data.chart) + } + data.namesMap = Array.isArray(data) ? data + .map(i => Object.keys(i)) + .flat() + .filter(i => i !== 'time' && i !== 'timestamp') + .reduce((unique: any, item: any) => { + if (!unique.includes(item)) { + unique.push(item); + } + return unique; + }, []) : data.chart; + console.log('map', data.namesMap) + const _data = { namesMap: data.namesMap, chart: data.chart } + metric.setData(_data) + resolve(_data); + } + }).catch((err) => { + console.log('err', err) + reject(err) + }) + }) + } +} \ No newline at end of file diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index bb4815389..48c796179 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -32,7 +32,6 @@ export interface IMetricStore { fetchList(): void fetch(metricId: string) delete(metric: IWidget) - // fetchMetricChartData(metric: IWidget) } export default class MetricStore implements IMetricStore { @@ -131,7 +130,7 @@ export default class MetricStore implements IMetricStore { // API Communication save(metric: IWidget, dashboardId?: string): Promise { - const wasCreating = !metric[Widget.ID_KEY] + const wasCreating = !metric.exists() this.isSaving = true return metricService.saveMetric(metric, dashboardId) .then((metric) => { @@ -177,20 +176,9 @@ export default class MetricStore implements IMetricStore { return metricService.deleteMetric(metric[Widget.ID_KEY]) .then(() => { this.removeById(metric[Widget.ID_KEY]) + toast.success('Metric deleted successfully') }).finally(() => { this.isSaving = false }) } - - fetchMetricChartData(metric: IWidget) { - this.isLoading = true - return metricService.getMetricChartData(metric) - .then(data => { - // runInAction(() => { - // metric.data = data - // }) - }).finally(() => { - this.isLoading = false - }) - } } \ No newline at end of file diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts index aefe7541f..e3db0a146 100644 --- a/frontend/app/mstore/types/dashboard.ts +++ b/frontend/app/mstore/types/dashboard.ts @@ -1,5 +1,7 @@ import { makeAutoObservable, observable, action, runInAction } from "mobx" import Widget, { IWidget } from "./widget" +import { dashboardService } from "App/services" +import { toast } from 'react-toastify'; export interface IDashboard { dashboardId: any @@ -24,7 +26,7 @@ export interface IDashboard { getWidgetByIndex(index: number): void getWidgetCount(): void getWidgetIndexByWidgetId(widgetId: string): void - swapWidgetPosition(positionA: number, positionB: number): void + swapWidgetPosition(positionA: number, positionB: number): Promise sortWidgets(): void exists(): boolean toggleMetrics(metricId: string): void @@ -93,7 +95,7 @@ export default class Dashboard implements IDashboard { this.name = json.name this.isPublic = json.isPublic this.isPinned = json.isPinned - this.config = json.config + // this.config = json.config this.widgets = json.widgets ? json.widgets.map(w => new Widget().fromJson(w)) : [] }) return this @@ -138,7 +140,7 @@ export default class Dashboard implements IDashboard { return this.widgets.findIndex(w => w.widgetId === widgetId) } - swapWidgetPosition(positionA, positionB) { + swapWidgetPosition(positionA, positionB): Promise { const widgetA = this.widgets[positionA] const widgetB = this.widgets[positionB] this.widgets[positionA] = widgetB @@ -146,6 +148,19 @@ export default class Dashboard implements IDashboard { widgetA.position = positionB widgetB.position = positionA + + return new Promise((resolve, reject) => { + Promise.all([ + dashboardService.saveWidget(this.dashboardId, widgetA), + dashboardService.saveWidget(this.dashboardId, widgetB) + ]).then(() => { + toast.success("Widget position updated") + resolve() + }).catch(() => { + toast.error("Error updating widget position") + reject() + }) + }) } sortWidgets() { diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index 672c84659..09d96d699 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -9,6 +9,7 @@ export interface IWidget { metricType: string metricOf: string metricValue: string + metricFormat: string viewType: string series: FilterSeries[] sessions: [] @@ -25,6 +26,7 @@ export interface IWidget { isValid: boolean dashboardId: any colSpan: number + predefinedKey: string udpateKey(key: string, value: any): void removeSeries(index: number): void @@ -34,6 +36,8 @@ export interface IWidget { validate(): void update(data: any): void exists(): boolean + toWidget(): any + setData(data: any): void } export default class Widget implements IWidget { public static get ID_KEY():string { return "metricId" } @@ -44,6 +48,7 @@ export default class Widget implements IWidget { metricOf: string = "sessionCount" metricValue: string = "" viewType: string = "lineChart" + metricFormat: string = "sessionCount" series: FilterSeries[] = [] sessions: [] = [] isPublic: boolean = true @@ -54,15 +59,19 @@ export default class Widget implements IWidget { config: any = {} position: number = 0 - data: any = {} + data: any = { + chart: [], + namesMap: {} + } isLoading: boolean = false isValid: boolean = false dashboardId: any = undefined colSpan: number = 2 + predefinedKey: string = '' constructor() { makeAutoObservable(this, { - // data: observable, + data: observable.ref, widgetId: observable, name: observable, metricType: observable, @@ -108,6 +117,8 @@ export default class Widget implements IWidget { this.metricValue = json.metricValue this.metricOf = json.metricOf this.metricType = json.metricType + this.metricFormat = json.metricFormat + this.viewType = json.viewType this.name = json.name this.series = json.series ? json.series.map((series: any) => new FilterSeries().fromJson(series)) : [], this.dashboards = json.dashboards @@ -116,10 +127,21 @@ export default class Widget implements IWidget { this.lastModified = DateTime.fromMillis(1649319074) this.config = json.config this.position = json.config.position + this.predefinedKey = json.predefinedKey }) return this } + toWidget(): any { + return { + config: { + position: this.position, + col: this.config.col, + row: this.config.row, + } + } + } + toJson() { return { metricId: this.metricId, @@ -127,6 +149,7 @@ export default class Widget implements IWidget { metricOf: this.metricOf, metricValue: this.metricValue, metricType: this.metricType, + metricFormat: this.metricFormat, viewType: this.viewType, name: this.name, series: this.series.map((series: any) => series.toJson()), @@ -146,4 +169,10 @@ export default class Widget implements IWidget { exists() { return this.metricId !== undefined } + + setData(data: any) { + runInAction(() => { + Object.assign(this.data, data) + }) + } } \ No newline at end of file diff --git a/frontend/app/services/DashboardService.ts b/frontend/app/services/DashboardService.ts index 3bb99b9b4..b75a059fa 100644 --- a/frontend/app/services/DashboardService.ts +++ b/frontend/app/services/DashboardService.ts @@ -17,6 +17,8 @@ export interface IDashboardService { addWidget(dashboard: IDashboard, metricIds: []): Promise saveWidget(dashboardId: string, widget: IWidget): Promise deleteWidget(dashboardId: string, widgetId: string): Promise + + updatePinned(dashboardId: string): Promise } @@ -115,8 +117,6 @@ export default class DashboardService implements IDashboardService { */ saveMetric(metric: IWidget, dashboardId?: string): Promise { const data = metric.toJson(); - - // const path = dashboardId ? `/metrics` : '/metrics'; // TODO change to /dashboards/:dashboardId/widgets const path = dashboardId ? `/dashboards/${dashboardId}/metrics` : '/metrics'; if (metric.widgetId) { return this.client.put(path + '/' + metric.widgetId, data) @@ -142,6 +142,23 @@ export default class DashboardService implements IDashboardService { * @returns {Promise} */ saveWidget(dashboardId: string, widget: IWidget): Promise { - return this.client.post(`/dashboards/${dashboardId}/widgets`, widget.toJson()) + if (widget.widgetId) { + return this.client.put(`/dashboards/${dashboardId}/widgets/${widget.widgetId}`, widget.toWidget()) + .then(response => response.json()) + .then(response => response.data || {}); + } + return this.client.post(`/dashboards/${dashboardId}/widgets`, widget.toWidget()) + .then(response => response.json()) + .then(response => response.data || {}); + } + + /** + * Update the pinned status of a dashboard. + * @param dashboardId + * @returns + */ + updatePinned(dashboardId: string): Promise { + return this.client.get(`/dashboards/${dashboardId}/pin`, {}) + .then(response => response.json()) } } \ No newline at end of file diff --git a/frontend/app/services/MetricService.ts b/frontend/app/services/MetricService.ts index f2fab0a83..a62520d52 100644 --- a/frontend/app/services/MetricService.ts +++ b/frontend/app/services/MetricService.ts @@ -10,7 +10,7 @@ export interface IMetricService { deleteMetric(metricId: string): Promise; getTemplates(): Promise; - getMetricChartData(metric: IWidget): Promise; + getMetricChartData(metric: IWidget, data: any, isWidget: boolean): Promise; } export default class MetricService implements IMetricService { @@ -54,18 +54,10 @@ export default class MetricService implements IMetricService { const data = metric.toJson() const isCreating = !data[Widget.ID_KEY]; const method = isCreating ? 'post' : 'put'; - - if(dashboardId) { - const url = `/dashboards/${dashboardId}/metrics`; - return this.client[method](url, data) - .then(response => response.json()) - .then(response => response.data || {}); - } else { - const url = isCreating ? '/metrics' : '/metrics/' + data[Widget.ID_KEY]; - return this.client[method](url, data) - .then(response => response.json()) - .then(response => response.data || {}); - } + const url = isCreating ? '/metrics' : '/metrics/' + data[Widget.ID_KEY]; + return this.client[method](url, data) + .then(response => response.json()) + .then(response => response.data || {}); } /** @@ -90,9 +82,9 @@ export default class MetricService implements IMetricService { .then(response => response.data || []); } - getMetricChartData(metric: IWidget): Promise { - const path = metric.metricId ? `/metrics/${metric.metricId}/chart` : `/custom_metrics/try`; - return this.client.get(path) + getMetricChartData(metric: IWidget, data: any, isWidget: boolean = false): Promise { + const path = isWidget ? `/metrics/${metric.metricId}/chart` : `/metrics/try`; + return this.client.post(path, data) .then(response => response.json()) .then(response => response.data || {}); } From 46199bbdcab5d21f2af7979b8180363691036dc8 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 8 Apr 2022 17:41:36 +0200 Subject: [PATCH 82/87] feat(db): changed dashboard template --- ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 4 ++-- ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql | 4 ++-- scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 4 ++-- scripts/helm/db/init_dbs/postgresql/init_schema.sql | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index f1d3cdc9a..8cafd767d 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -102,7 +102,7 @@ VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, ('Pages Response Time', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), ('Pages Response Time Distribution', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), - ('Missing Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), + ('Missing Resources', 'resources', '{"col":4,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), ('Slowest Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), ('Resources Fetch Time', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_loading_time', 'predefined', 'table'), ('Resource Loaded vs Response End', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resource_type_vs_response_end', 'predefined', 'stackedBarLineChart'), @@ -110,7 +110,7 @@ VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, - config=excluded.config, + default_config=excluded.default_config, is_predefined=excluded.is_predefined, is_template=excluded.is_template, is_public=excluded.is_public, diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index f2cb95b76..5abd6f026 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1302,14 +1302,14 @@ VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, ('Pages Response Time Distribution', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), ('Missing Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), - ('Slowest Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), + ('Slowest Resources', 'resources', '{"col":4,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), ('Resources Fetch Time', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_loading_time', 'predefined', 'table'), ('Resource Loaded vs Response End', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resource_type_vs_response_end', 'predefined', 'stackedBarLineChart'), ('Breakdown of Loaded Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_count_by_type', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, - config=excluded.config, + default_config=excluded.default_config, is_predefined=excluded.is_predefined, is_template=excluded.is_template, is_public=excluded.is_public, diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index c97cd76ac..c50f6fc42 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -102,14 +102,14 @@ VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, ('Pages Response Time Distribution', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), ('Missing Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), - ('Slowest Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), + ('Slowest Resources', 'resources', '{"col":4,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), ('Resources Fetch Time', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_loading_time', 'predefined', 'table'), ('Resource Loaded vs Response End', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resource_type_vs_response_end', 'predefined', 'stackedBarLineChart'), ('Breakdown of Loaded Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_count_by_type', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, - config=excluded.config, + default_config=excluded.default_config, is_predefined=excluded.is_predefined, is_template=excluded.is_template, is_public=excluded.is_public, diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 3d5239153..d78d0754b 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1093,14 +1093,14 @@ VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, ('Pages Response Time Distribution', 'performance', '{"col":2,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), ('Missing Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'missing_resources', 'predefined', 'table'), - ('Slowest Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), + ('Slowest Resources', 'resources', '{"col":4,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), ('Resources Fetch Time', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_loading_time', 'predefined', 'table'), ('Resource Loaded vs Response End', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resource_type_vs_response_end', 'predefined', 'stackedBarLineChart'), ('Breakdown of Loaded Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_count_by_type', 'predefined', 'stackedBarChart') ON CONFLICT (predefined_key) DO UPDATE SET name=excluded.name, category=excluded.category, - config=excluded.config, + default_config=excluded.default_config, is_predefined=excluded.is_predefined, is_template=excluded.is_template, is_public=excluded.is_public, From e362cde1a99bac9e7d40b4dd32ee4523c0ec2c90 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 8 Apr 2022 17:54:13 +0200 Subject: [PATCH 83/87] feat(api): dashboard merge config if not present in payload feat(api): dashboard use metrics' default config for batch create --- api/chalicelib/core/dashboards2.py | 13 +++++++------ api/schemas.py | 3 +-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 651500aaa..c82a38b5f 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -48,14 +48,15 @@ def create_dashboard(project_id, user_id, data: schemas.CreateDashboardSchema): if data.metrics is not None and len(data.metrics) > 0: pg_query = f"""WITH dash AS ({pg_query}) INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config) - VALUES {",".join([f"((SELECT dashboard_id FROM dash),%(metric_id_{i})s, %(userId)s, %(config_{i})s)" for i in range(len(data.metrics))])} + VALUES {",".join([f"((SELECT dashboard_id FROM dash),%(metric_id_{i})s, %(userId)s, (SELECT default_config FROM metrics WHERE metric_id=%(metric_id_{i})s)||%(config_{i})s))" for i in range(len(data.metrics))])} RETURNING (SELECT dashboard_id FROM dash)""" for i, m in enumerate(data.metrics): params[f"metric_id_{i}"] = m - params[f"config_{i}"] = schemas.AddWidgetToDashboardPayloadSchema.schema() \ - .get("properties", {}).get("config", {}).get("default", {}) - params[f"config_{i}"]["position"] = i - params[f"config_{i}"] = json.dumps(params[f"config_{i}"]) + # params[f"config_{i}"] = schemas.AddWidgetToDashboardPayloadSchema.schema() \ + # .get("properties", {}).get("config", {}).get("default", {}) + # params[f"config_{i}"]["position"] = i + # params[f"config_{i}"] = json.dumps(params[f"config_{i}"]) + params[f"config_{i}"] = json.dumps({"position": i}) cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() if row is None: @@ -173,7 +174,7 @@ def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashb with pg_client.PostgresClient() as cur: pg_query = """INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config) SELECT %(dashboard_id)s AS dashboard_id, %(metric_id)s AS metric_id, - %(userId)s AS user_id, %(config)s::jsonb AS config + %(userId)s AS user_id, (SELECT default_config FROM metrics WHERE metric_id=%(metric_id)s)||%(config)s::jsonb AS config WHERE EXISTS(SELECT 1 FROM dashboards WHERE dashboards.deleted_at ISNULL AND dashboards.project_id = %(projectId)s AND dashboard_id = %(dashboard_id)s diff --git a/api/schemas.py b/api/schemas.py index 7ba9c0413..b01553cc8 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -898,8 +898,7 @@ class EditDashboardSchema(CreateDashboardSchema): class UpdateWidgetPayloadSchema(BaseModel): - # if you change the config attribute name, please make sure to update it in dashboard2.py - config: dict = Field(default={"col": 2, "row": 2, "position": 0}) + config: dict = Field(default={}) class Config: alias_generator = attribute_to_camel_case From 7dd96fde02ee61068cfeb951132e14d30465d532 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 8 Apr 2022 18:09:44 +0200 Subject: [PATCH 84/87] feat(api): dashboard fixed SQL typo --- api/chalicelib/core/dashboards2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index c82a38b5f..231f20519 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -48,7 +48,7 @@ def create_dashboard(project_id, user_id, data: schemas.CreateDashboardSchema): if data.metrics is not None and len(data.metrics) > 0: pg_query = f"""WITH dash AS ({pg_query}) INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config) - VALUES {",".join([f"((SELECT dashboard_id FROM dash),%(metric_id_{i})s, %(userId)s, (SELECT default_config FROM metrics WHERE metric_id=%(metric_id_{i})s)||%(config_{i})s))" for i in range(len(data.metrics))])} + VALUES {",".join([f"((SELECT dashboard_id FROM dash),%(metric_id_{i})s, %(userId)s, (SELECT default_config FROM metrics WHERE metric_id=%(metric_id_{i})s)||%(config_{i})s)" for i in range(len(data.metrics))])} RETURNING (SELECT dashboard_id FROM dash)""" for i, m in enumerate(data.metrics): params[f"metric_id_{i}"] = m From 1b9df5e6bd4a0cd01e25d6e450eba4106a7713b2 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 8 Apr 2022 18:11:55 +0200 Subject: [PATCH 85/87] feat(ui) - dashboard - wip --- .../CustomMetricOverviewChart.tsx | 15 +-- .../CustomMetricPieChart.tsx | 2 +- .../BreakdownOfLoadedResources.tsx | 48 +++++++ .../BreakdownOfLoadedResources/index.ts | 1 + .../MissingResources/Chart.js | 16 +++ .../MissingResources/CopyPath.js | 23 ++++ .../MissingResources/MissingResources.tsx | 62 +++++++++ .../MissingResources/ResourceInfo.js | 18 +++ .../MissingResources/index.ts | 1 + .../MissingResources/resourceInfo.css | 10 ++ .../ResourceLoadedVsResponseEnd.tsx | 70 ++++++++++ .../ResourceLoadedVsResponseEnd/index.ts | 1 + .../ResourceLoadedVsVisuallyComplete.tsx | 73 +++++++++++ .../ResourceLoadedVsVisuallyComplete/index.ts | 1 + .../ResourceLoadingTime.tsx | 122 ++++++++++++++++++ .../ResourceLoadingTime/index.ts | 1 + .../SessionsImpactedBySlowRequests.tsx | 55 ++++++++ .../SessionsImpactedBySlowRequests/index.ts | 1 + .../TimeToRender/TimeToRender.tsx | 2 +- .../DashboardMetricSelection.tsx | 2 +- .../components/WidgetChart/WidgetChart.tsx | 4 + .../WidgetPredefinedChart.tsx | 54 +++++--- .../app/components/ui/NoContent/NoContent.js | 5 +- frontend/app/mstore/dashboardStore.ts | 80 ++++++++---- frontend/app/mstore/types/widget.ts | 1 + 25 files changed, 610 insertions(+), 58 deletions(-) create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/BreakdownOfLoadedResources/BreakdownOfLoadedResources.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/BreakdownOfLoadedResources/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/Chart.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/CopyPath.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/MissingResources.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/ResourceInfo.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/resourceInfo.css create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/ResourceLoadedVsResponseEnd.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete/ResourceLoadedVsVisuallyComplete.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/index.ts diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx index 3dad11dc5..93e0fbf6d 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx @@ -8,26 +8,18 @@ import { numberWithCommas } from 'App/utils'; interface Props { data: any; - params: any; - seriesMap: any; - colors: any; - onClick?: (event, index) => void; + // onClick?: (event, index) => void; } function CustomMetricOverviewChart(props: Props) { - const { data, params, seriesMap, colors, onClick = () => null } = props; + const { data } = props; console.log('data', data) const gradientDef = Styles.gradientDef(); return ( -
+
- {/*
{ 'test' }
*/}
- {/* {prefix} */} - {/*
-
-
*/} + + + + {gradientDef} + + + + + + + + + + + + ); +} + +export default BreakdownOfLoadedResources; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/BreakdownOfLoadedResources/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/BreakdownOfLoadedResources/index.ts new file mode 100644 index 000000000..5770a63d8 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/BreakdownOfLoadedResources/index.ts @@ -0,0 +1 @@ +export { default } from './BreakdownOfLoadedResources' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/Chart.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/Chart.js new file mode 100644 index 000000000..2f406622d --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/Chart.js @@ -0,0 +1,16 @@ +import { AreaChart, Area } from 'recharts'; +import { Styles } from '../../common'; + +const Chart = ({ data, compare }) => { + const colors = compare ? Styles.compareColors : Styles.colors; + + return ( + + + + ); +} + +Chart.displayName = 'Chart'; + +export default Chart; diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/CopyPath.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/CopyPath.js new file mode 100644 index 000000000..6b7e709e7 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/CopyPath.js @@ -0,0 +1,23 @@ +import React from 'react' +import copy from 'copy-to-clipboard' +import { useState } from 'react' + +const CopyPath = ({ data }) => { + const [copied, setCopied] = useState(false) + + const copyHandler = () => { + copy(data.url); + setCopied(true); + setTimeout(function() { + setCopied(false) + }, 500); + } + + return ( +
+ { copied ? 'Copied' : 'Copy Path'} +
+ ) +} + +export default CopyPath diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/MissingResources.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/MissingResources.tsx new file mode 100644 index 000000000..ce3544f3e --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/MissingResources.tsx @@ -0,0 +1,62 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles, Table } from '../../common'; +import { List } from 'immutable'; + +import Chart from './Chart'; +import ResourceInfo from './ResourceInfo'; +import CopyPath from './CopyPath'; + +const cols = [ + { + key: 'resource', + title: 'Resource', + Component: ResourceInfo, + width: '40%', + }, + { + key: 'sessions', + title: 'Sessions', + toText: count => `${ count > 1000 ? Math.trunc(count / 1000) : count }${ count > 1000 ? 'k' : '' }`, + width: '20%', + }, + { + key: 'trend', + title: 'Trend', + Component: Chart, + width: '20%', + }, + { + key: 'copy-path', + title: '', + Component: CopyPath, + cellClass: 'invisible group-hover:visible text-right', + width: '20%', + } +]; + +interface Props { + data: any +} +function MissingResources(props: Props) { + const { data } = props; + + return ( + +
+ + + + ); +} + +export default MissingResources; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/ResourceInfo.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/ResourceInfo.js new file mode 100644 index 000000000..d4b1ed9b8 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/ResourceInfo.js @@ -0,0 +1,18 @@ +import { diffFromNowString } from 'App/date'; +import { TextEllipsis } from 'UI'; + +import styles from './resourceInfo.css'; + +export default class ResourceInfo extends React.PureComponent { + render() { + const { data } = this.props; + return ( +
+ +
+ { data.endedAt && data.startedAt && `${ diffFromNowString(data.endedAt) } ago - ${ diffFromNowString(data.startedAt) } old` } +
+
+ ); + } +} diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/index.ts new file mode 100644 index 000000000..db419a09a --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/index.ts @@ -0,0 +1 @@ +export { default } from './MissingResources' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/resourceInfo.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/resourceInfo.css new file mode 100644 index 000000000..d73d23530 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/MissingResources/resourceInfo.css @@ -0,0 +1,10 @@ +.name { + letter-spacing: -.04em; + font-size: .9rem; + cursor: pointer; +} + +.timings { + color: $gray-medium; + font-size: 12px; +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/ResourceLoadedVsResponseEnd.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/ResourceLoadedVsResponseEnd.tsx new file mode 100644 index 000000000..378a3abdc --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/ResourceLoadedVsResponseEnd.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + ComposedChart, Bar, CartesianGrid, Line, Legend, ResponsiveContainer, + XAxis, YAxis, Tooltip +} from 'recharts'; + +interface Props { + data: any +} +function ResourceLoadedVsResponseEnd(props: Props) { + const { data } = props; + const params = { density: 70 } + + return ( + + + + + + Styles.tickFormatter(val, 'ms')} + /> + Styles.tickFormatter(val, 'ms')} + /> + + + + + + + + + ); +} + +export default ResourceLoadedVsResponseEnd; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/index.ts new file mode 100644 index 000000000..072096a6f --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/index.ts @@ -0,0 +1 @@ +export { default } from './ResourceLoadedVsResponseEnd' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete/ResourceLoadedVsVisuallyComplete.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete/ResourceLoadedVsVisuallyComplete.tsx new file mode 100644 index 000000000..cab6b10ee --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete/ResourceLoadedVsVisuallyComplete.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + ComposedChart, Bar, CartesianGrid, Line, Legend, ResponsiveContainer, + XAxis, YAxis, Tooltip +} from 'recharts'; + +interface Props { + data: any +} +function ResourceLoadedVsVisuallyComplete(props: Props) { + const { data } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + + return ( + + + + + + Styles.tickFormatter(val, 'ms')} + /> + Styles.tickFormatter(val)} + /> + + + + + + + + + + ); +} + +export default ResourceLoadedVsVisuallyComplete; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete/index.ts new file mode 100644 index 000000000..af77c13fa --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete/index.ts @@ -0,0 +1 @@ +export { default } from './ResourceLoadedVsVisuallyComplete' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx new file mode 100644 index 000000000..f83b7c01f --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx @@ -0,0 +1,122 @@ +import React from 'react'; +import { NoContent, DropdownPlain } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import { withRequest } from 'HOCs' +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; +import WidgetAutoComplete from 'Shared/WidgetAutoComplete'; +import { toUnderscore } from 'App/utils'; + +const WIDGET_KEY = 'resourcesLoadingTime'; +export const RESOURCE_OPTIONS = [ + { text: 'All', value: 'all', }, + { text: 'JS', value: "SCRIPT", }, + { text: 'CSS', value: "STYLESHEET", }, + { text: 'Fetch', value: "REQUEST", }, + { text: 'Image', value: "IMG", }, + { text: 'Media', value: "MEDIA", }, + { text: 'Other', value: "OTHER", }, +]; + +interface Props { + data: any + optionsLoading: any + fetchOptions: any + options: any +} +function ResourceLoadingTime(props: Props) { + const { data, optionsLoading } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + const [autoCompleteSelected, setSutoCompleteSelected] = React.useState(''); + const [type, setType] = React.useState(''); + + const onSelect = (params) => { + const _params = { density: 70 } + setSutoCompleteSelected(params.value); + console.log('params', params) // TODO reload the data with new params; + // this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value }) + } + + const writeOption = (e, { name, value }) => { + // this.setState({ [name]: value }) + setType(value); + const _params = { density: 70 } // TODO reload the data with new params; + // this.props.fetchWidget(WIDGET_KEY, this.props.period, this.props.platform, { ..._params, [ name ]: value === 'all' ? null : value }) + } + + return ( + + <> +
+ + + +
+ + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + /> + + + + + +
+ ); +} + +export default withRequest({ + dataName: "options", + initialData: [], + dataWrapper: data => data, + loadingName: 'optionsLoading', + requestName: "fetchOptions", + endpoint: '/dashboard/' + toUnderscore(WIDGET_KEY) + '/search', + method: 'GET' +})(ResourceLoadingTime) \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/index.ts new file mode 100644 index 000000000..1c9fa51c8 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/index.ts @@ -0,0 +1 @@ +export { default } from './ResourceLoadingTime' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx new file mode 100644 index 000000000..5314a054b --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles } from '../../common'; +import { + AreaChart, Area, + BarChart, Bar, CartesianGrid, Tooltip, + LineChart, Line, Legend, ResponsiveContainer, + XAxis, YAxis + } from 'recharts'; + +interface Props { + data: any +} +function SessionsImpactedBySlowRequests(props: Props) { + const { data } = props; + const gradientDef = Styles.gradientDef(); + const params = { density: 70 } + + return ( + + + + {gradientDef} + + + Styles.tickFormatter(val)} + label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + /> + + + + + + ); +} + +export default SessionsImpactedBySlowRequests; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/index.ts new file mode 100644 index 000000000..d950b82ae --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/index.ts @@ -0,0 +1 @@ +export { default } from './SessionsImpactedBySlowRequests' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx index 5332f71c5..1a3ab28ab 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx @@ -47,7 +47,7 @@ function TimeToRender(props: Props) { /> - +
{activeCategory && activeCategory.widgets.map((widget: any) => ( diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index 86bffb9e9..a0d4dd75e 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -8,6 +8,7 @@ import { observer, useObserver, useLocalObservable } from 'mobx-react-lite'; import { Loader } from 'UI'; import { useStore } from 'App/mstore'; import WidgetPredefinedChart from '../WidgetPredefinedChart'; +import CustomMetricOverviewChart from '../../Widgets/CustomMetricsWidgets/CustomMetricOverviewChart'; interface Props { metric: any; isWidget?: boolean @@ -45,6 +46,9 @@ function WidgetChart(props: Props) { const { metricType, viewType, predefinedKey } = metric; if (metricType === 'predefined') { + if (viewType === 'overview') { + return + } return } diff --git a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx index da6c49280..7ae8a581f 100644 --- a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx @@ -5,17 +5,23 @@ import ErrorsByType from 'App/components/Dashboard/Widgets/PredefinedWidgets/Err import ErrorsByOrigin from 'App/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin'; import ErrorsPerDomain from 'App/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain'; import { useObserver } from 'mobx-react-lite'; -import SessionsAffectedByJSErrors from '../../Widgets/PredefinedWidgets/SessionsAffectedByJSErrors'; -import CallsErrors4xx from '../../Widgets/PredefinedWidgets/CallsErrors4xx'; -import CallsErrors5xx from '../../Widgets/PredefinedWidgets/CallsErrors5xx'; -import CPULoad from '../../Widgets/PredefinedWidgets/CPULoad'; -import Crashes from '../../Widgets/PredefinedWidgets/Crashes'; -import DomBuildingTime from '../../Widgets/PredefinedWidgets/DomBuildingTime'; -import FPS from '../../Widgets/PredefinedWidgets/FPS'; -import MemoryConsumption from '../../Widgets/PredefinedWidgets/MemoryConsumption'; -import ResponseTime from '../../Widgets/PredefinedWidgets/ResponseTime'; -import TimeToRender from '../../Widgets/PredefinedWidgets/TimeToRender'; -import SlowestDomains from '../../Widgets/PredefinedWidgets/SlowestDomains'; +import SessionsAffectedByJSErrors from 'App/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors'; +import CallsErrors4xx from 'App/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx'; +import CallsErrors5xx from 'App/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx'; +import CPULoad from 'App/components/Dashboard/Widgets/PredefinedWidgets/CPULoad'; +import Crashes from 'App/components/Dashboard/Widgets/PredefinedWidgets/Crashes'; +import DomBuildingTime from 'App/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime'; +import FPS from 'App/components/Dashboard/Widgets/PredefinedWidgets/FPS'; +import MemoryConsumption from 'App/components/Dashboard/Widgets/PredefinedWidgets/MemoryConsumption'; +import ResponseTime from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime'; +import TimeToRender from 'App/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender'; +import SlowestDomains from 'App/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains'; +import ResourceLoadedVsVisuallyComplete from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsVisuallyComplete'; +import SessionsImpactedBySlowRequests from 'App/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests'; +import ResourceLoadingTime from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime'; +import BreakdownOfLoadedResources from 'App/components/Dashboard/Widgets/PredefinedWidgets/BreakdownOfLoadedResources'; +import MissingResources from 'App/components/Dashboard/Widgets/PredefinedWidgets/MissingResources'; +import ResourceLoadedVsResponseEnd from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd'; interface Props { data: any; @@ -23,8 +29,6 @@ interface Props { } function WidgetPredefinedChart(props: Props) { const { data, predefinedKey } = props; - // const { viewType } = data; - const params = { density: 70 } const renderWidget = () => { switch (predefinedKey) { @@ -43,6 +47,9 @@ function WidgetPredefinedChart(props: Props) { return // PERFORMANCE + // case 'impacted_sessions_by_slow_pages': + // case 'pages_response_time_distribution': + // case 'speed_location': case 'cpu': return case 'crashes': @@ -55,19 +62,28 @@ function WidgetPredefinedChart(props: Props) { return case 'pages_response_time': return - // case 'pages_response_time_distribution': - // case 'resources_vs_visually_complete': - // case 'impacted_sessions_by_slow_pages': - // case 'sessions_per_browser': + case 'resources_vs_visually_complete': + return + case 'sessions_per_browser': + return case 'slowest_domains': return - // case 'speed_location': case 'time_to_render': return + // Resources + case 'resources_count_by_type': + return + case 'missing_resources': + return + case 'resource_type_vs_response_end': + return + case 'resources_loading_time': + return + // case 'slowest_resources': default: - return
No widget found
+ return
Widget not supported
} } diff --git a/frontend/app/components/ui/NoContent/NoContent.js b/frontend/app/components/ui/NoContent/NoContent.js index 8c14a0917..83b4f53f3 100644 --- a/frontend/app/components/ui/NoContent/NoContent.js +++ b/frontend/app/components/ui/NoContent/NoContent.js @@ -9,9 +9,10 @@ export default ({ show = true, children = null, empty = false, - image = null + image = null, + style = {}, }) => (!show ? children : -
+
{ icon &&
} diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 6f2e35da2..0231bbf7c 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -193,19 +193,25 @@ export default class DashboardStore implements IDashboardSotre { dashboard.metrics = this.selectedWidgets.map(w => w.metricId) - return dashboardService.saveDashboard(dashboard).then(_dashboard => { - runInAction(() => { - if (isCreating) { - toast.success('Dashboard created successfully') - this.addDashboard(_dashboard) - } else { - toast.success('Dashboard updated successfully') - this.updateDashboard(_dashboard) - } - }) - }).finally(() => { - runInAction(() => { - this.isSaving = false + return new Promise((resolve, reject) => { + dashboardService.saveDashboard(dashboard).then(_dashboard => { + runInAction(() => { + if (isCreating) { + toast.success('Dashboard created successfully') + this.addDashboard(_dashboard) + } else { + toast.success('Dashboard updated successfully') + this.updateDashboard(_dashboard) + } + resolve(_dashboard) + }) + }).catch(error => { + toast.error('Error saving dashboard') + reject() + }).finally(() => { + runInAction(() => { + this.isSaving = false + }) }) }) } @@ -406,12 +412,12 @@ export default class DashboardStore implements IDashboardSotre { metric.setData(_data) resolve(_data); } else { - if (metric.predefinedKey === 'errors_per_domains') { - console.log('errors_per_domains', data) - data.chart = data - } else { - data.chart = getChartFormatter(this.period)(Array.isArray(data) ? data : data.chart) - } + // if (metric.predefinedKey === 'errors_per_domains') { + // console.log('errors_per_domains', data) + // data.chart = data + // } else { + // data.chart = getChartFormatter(this.period)(Array.isArray(data) ? data : data.chart) + // } data.namesMap = Array.isArray(data) ? data .map(i => Object.keys(i)) .flat() @@ -422,10 +428,40 @@ export default class DashboardStore implements IDashboardSotre { } return unique; }, []) : data.chart; - console.log('map', data.namesMap) - const _data = { namesMap: data.namesMap, chart: data.chart } + // console.log('map', data.namesMap) + // const _data = { ...data, namesMap: data.namesMap, chart: data.chart } + // metric.setData(_data) + // resolve(_data); + + const _data = {} + if (data.hasOwnProperty('chart')) { + _data['chart'] = getChartFormatter(this.period)(data.chart) + _data['namesMap'] = data.chart + .map(i => Object.keys(i)) + .flat() + .filter(i => i !== 'time' && i !== 'timestamp') + .reduce((unique: any, item: any) => { + if (!unique.includes(item)) { + unique.push(item); + } + return unique; + }, []) + } else { + _data['chart'] = data + _data['namesMap'] = data + .map(i => Object.keys(i)) + .flat() + .filter(i => i !== 'time' && i !== 'timestamp') + .reduce((unique: any, item: any) => { + if (!unique.includes(item)) { + unique.push(item); + } + return unique; + }, []) + } + metric.setData(_data) - resolve(_data); + resolve({ ...data, ..._data }); } }).catch((err) => { console.log('err', err) diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index 09d96d699..ee9c559b4 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -111,6 +111,7 @@ export default class Widget implements IWidget { } fromJson(json: any) { + json.config = json.config || {} runInAction(() => { this.metricId = json.metricId this.widgetId = json.widgetId From f52a61babc3775609f9a39035a803873c24672ba Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 8 Apr 2022 18:21:58 +0200 Subject: [PATCH 86/87] Update frontend.yaml --- .github/workflows/frontend.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/frontend.yaml b/.github/workflows/frontend.yaml index 8c5038f5f..990ce3c8a 100644 --- a/.github/workflows/frontend.yaml +++ b/.github/workflows/frontend.yaml @@ -1,5 +1,6 @@ name: Frontend FOSS Deployment on: + workflow_dispatch: push: branches: - dev From ea6e21b94c12c5611102c648764947edf9ede1f8 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 8 Apr 2022 18:41:42 +0200 Subject: [PATCH 87/87] feat(api): dashboard fixed update with empty config payload --- api/chalicelib/core/dashboards2.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 231f20519..a66324532 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -133,13 +133,14 @@ def update_dashboard(project_id, user_id, dashboard_id, data: schemas.EditDashbo if data.metrics is not None and len(data.metrics) > 0: pg_query = f"""WITH dash AS ({pg_query}) INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config) - VALUES {",".join([f"(%(dashboard_id)s, %(metric_id_{i})s, %(userId)s, %(config_{i})s)" for i in range(len(data.metrics))])};""" + VALUES {",".join([f"(%(dashboard_id)s, %(metric_id_{i})s, %(userId)s, (SELECT default_config FROM metrics WHERE metric_id=%(metric_id_{i})s)||%(config_{i})s)" for i in range(len(data.metrics))])};""" for i, m in enumerate(data.metrics): params[f"metric_id_{i}"] = m - params[f"config_{i}"] = schemas.AddWidgetToDashboardPayloadSchema.schema() \ - .get("properties", {}).get("config", {}).get("default", {}) - params[f"config_{i}"]["position"] = i - params[f"config_{i}"] = json.dumps(params[f"config_{i}"]) + # params[f"config_{i}"] = schemas.AddWidgetToDashboardPayloadSchema.schema() \ + # .get("properties", {}).get("config", {}).get("default", {}) + # params[f"config_{i}"]["position"] = i + # params[f"config_{i}"] = json.dumps(params[f"config_{i}"]) + params[f"config_{i}"] = json.dumps({"position": i}) cur.execute(cur.mogrify(pg_query, params))