From 38b536aae152e1464009fd56dbf916efce8e5434 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 18 Mar 2022 13:31:02 +0100 Subject: [PATCH 001/294] 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 002/294] 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 94370f028f09ccab9f29c15b2ec5f9a0298c1c68 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 21 Mar 2022 12:28:27 +0100 Subject: [PATCH 003/294] 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 004/294] 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 005/294] 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 006/294] 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 007/294] 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 008/294] 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 009/294] 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 010/294] 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 011/294] 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 012/294] 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 013/294] 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 014/294] 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 015/294] 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 016/294] 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 017/294] 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 018/294] 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 019/294] 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 020/294] 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 021/294] 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 022/294] 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 8c975769702f7d355aecc391c211156f0ce8505d Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Wed, 30 Mar 2022 18:27:16 +0200 Subject: [PATCH 023/294] codefix(backend): autoCommit as init consumer parameter --- backend/pkg/queue/import.go | 5 +-- backend/pkg/queue/messages.go | 5 +-- backend/pkg/queue/types/types.go | 8 +--- backend/pkg/redisstream/consumer.go | 47 +++++++++------------ backend/services/assets/main.go | 18 ++++---- backend/services/db/main.go | 2 +- backend/services/ender/main.go | 21 +++++----- backend/services/sink/main.go | 64 ++++++++++++++--------------- backend/services/storage/main.go | 48 +++++++++------------- ee/backend/pkg/kafka/consumer.go | 28 ++++++++----- ee/backend/pkg/queue/import.go | 7 ++-- 11 files changed, 117 insertions(+), 136 deletions(-) diff --git a/backend/pkg/queue/import.go b/backend/pkg/queue/import.go index 2bca9c8fd..623d301ca 100644 --- a/backend/pkg/queue/import.go +++ b/backend/pkg/queue/import.go @@ -1,15 +1,14 @@ package queue import ( - "openreplay/backend/pkg/redisstream" "openreplay/backend/pkg/queue/types" + "openreplay/backend/pkg/redisstream" ) -func NewConsumer(group string, topics []string, handler types.MessageHandler) types.Consumer { +func NewConsumer(group string, topics []string, handler types.MessageHandler, _ bool) types.Consumer { return redisstream.NewConsumer(group, topics, handler) } func NewProducer() types.Producer { return redisstream.NewProducer() } - diff --git a/backend/pkg/queue/messages.go b/backend/pkg/queue/messages.go index eca4a4d49..0ab184ee6 100644 --- a/backend/pkg/queue/messages.go +++ b/backend/pkg/queue/messages.go @@ -7,13 +7,12 @@ import ( "openreplay/backend/pkg/queue/types" ) - -func NewMessageConsumer(group string, topics []string, handler types.DecodedMessageHandler) types.Consumer { +func NewMessageConsumer(group string, topics []string, handler types.DecodedMessageHandler, autoCommit bool) types.Consumer { return NewConsumer(group, topics, func(sessionID uint64, value []byte, meta *types.Meta) { if err := messages.ReadBatch(value, func(msg messages.Message) { handler(sessionID, msg, meta) }); err != nil { log.Printf("Decode error: %v\n", err) } - }) + }, autoCommit) } diff --git a/backend/pkg/queue/types/types.go b/backend/pkg/queue/types/types.go index b671323d0..600babe25 100644 --- a/backend/pkg/queue/types/types.go +++ b/backend/pkg/queue/types/types.go @@ -6,26 +6,22 @@ import ( type Consumer interface { ConsumeNext() error - DisableAutoCommit() Commit() error CommitBack(gap int64) error Close() } - type Producer interface { Produce(topic string, key uint64, value []byte) error Close(timeout int) Flush(timeout int) } - type Meta struct { - ID uint64 - Topic string + ID uint64 + Topic string Timestamp int64 } type MessageHandler func(uint64, []byte, *Meta) type DecodedMessageHandler func(uint64, messages.Message, *Meta) - diff --git a/backend/pkg/redisstream/consumer.go b/backend/pkg/redisstream/consumer.go index 164ee9236..d32972981 100644 --- a/backend/pkg/redisstream/consumer.go +++ b/backend/pkg/redisstream/consumer.go @@ -1,24 +1,22 @@ package redisstream import ( + "log" "net" + "sort" "strconv" "strings" - "log" - "sort" "time" - "github.com/pkg/errors" _redis "github.com/go-redis/redis" + "github.com/pkg/errors" "openreplay/backend/pkg/queue/types" ) - - -type idsInfo struct{ - id []string - ts []int64 +type idsInfo struct { + id []string + ts []int64 } type streamPendingIDsMap map[string]*idsInfo @@ -41,26 +39,25 @@ func NewConsumer(group string, streams []string, messageHandler types.MessageHan } } - idsPending := make(streamPendingIDsMap) streamsCount := len(streams) for i := 0; i < streamsCount; i++ { - // ">" is for never-delivered messages. - // Otherwise - never acknoledged only + // ">" is for never-delivered messages. + // Otherwise - never acknoledged only // TODO: understand why in case of "0" it eats 100% cpu - streams = append(streams, ">") - + streams = append(streams, ">") + idsPending[streams[i]] = new(idsInfo) } return &Consumer{ - redis: redis, + redis: redis, messageHandler: messageHandler, - streams: streams, - group: group, - autoCommit: true, - idsPending: idsPending, + streams: streams, + group: group, + autoCommit: true, + idsPending: idsPending, } } @@ -106,9 +103,9 @@ func (c *Consumer) ConsumeNext() error { return errors.New("Too many messages per ms in redis") } c.messageHandler(sessionID, []byte(valueString), &types.Meta{ - Topic: r.Stream, + Topic: r.Stream, Timestamp: int64(ts), - ID: ts << 13 | (idx & 0x1FFF), // Max: 4096 messages/ms for 69 years + ID: ts<<13 | (idx & 0x1FFF), // Max: 4096 messages/ms for 69 years }) if c.autoCommit { if err = c.redis.XAck(r.Stream, c.group, m.ID).Err(); err != nil { @@ -119,7 +116,7 @@ func (c *Consumer) ConsumeNext() error { c.idsPending[r.Stream].id = append(c.idsPending[r.Stream].id, m.ID) c.idsPending[r.Stream].ts = append(c.idsPending[r.Stream].ts, int64(ts)) } - + } } return nil @@ -158,13 +155,9 @@ func (c *Consumer) CommitBack(gap int64) error { c.idsPending[stream].id = idsInfo.id[maxI:] c.idsPending[stream].ts = idsInfo.ts[maxI:] } - return nil -} - -func (c *Consumer) DisableAutoCommit() { - //c.autoCommit = false + return nil } func (c *Consumer) Close() { // noop -} \ No newline at end of file +} diff --git a/backend/services/assets/main.go b/backend/services/assets/main.go index 450dfc83c..664dc5b09 100644 --- a/backend/services/assets/main.go +++ b/backend/services/assets/main.go @@ -18,7 +18,7 @@ import ( func main() { log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile) - GROUP_CACHE := env.String("GROUP_CACHE") + GROUP_CACHE := env.String("GROUP_CACHE") TOPIC_CACHE := env.String("TOPIC_CACHE") cacher := cacher.NewCacher( @@ -29,10 +29,10 @@ func main() { ) consumer := queue.NewMessageConsumer( - GROUP_CACHE, - []string{ TOPIC_CACHE }, + GROUP_CACHE, + []string{TOPIC_CACHE}, func(sessionID uint64, message messages.Message, e *types.Meta) { - switch msg := message.(type) { + switch msg := message.(type) { case *messages.AssetCache: cacher.CacheURL(sessionID, msg.URL) case *messages.ErrorEvent: @@ -47,17 +47,17 @@ func main() { for _, source := range sourceList { cacher.CacheJSFile(source) } - } + } }, + true, ) - tick := time.Tick(20 * time.Minute) sigchan := make(chan os.Signal, 1) - signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) - log.Printf("Cacher service started\n") + log.Printf("Cacher service started\n") for { select { case sig := <-sigchan: @@ -74,4 +74,4 @@ func main() { } } } -} \ No newline at end of file +} diff --git a/backend/services/db/main.go b/backend/services/db/main.go index d6190a4f0..2ad6e4aa8 100644 --- a/backend/services/db/main.go +++ b/backend/services/db/main.go @@ -74,8 +74,8 @@ func main() { } }) }, + false, ) - consumer.DisableAutoCommit() sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) diff --git a/backend/services/ender/main.go b/backend/services/ender/main.go index e8d739f0e..f0f139dce 100644 --- a/backend/services/ender/main.go +++ b/backend/services/ender/main.go @@ -8,12 +8,12 @@ import ( "os/signal" "syscall" - "openreplay/backend/pkg/intervals" "openreplay/backend/pkg/env" + "openreplay/backend/pkg/intervals" + logger "openreplay/backend/pkg/log" "openreplay/backend/pkg/messages" "openreplay/backend/pkg/queue" "openreplay/backend/pkg/queue/types" - logger "openreplay/backend/pkg/log" "openreplay/backend/services/ender/builder" ) @@ -29,24 +29,24 @@ func main() { producer := queue.NewProducer() consumer := queue.NewMessageConsumer( - GROUP_EVENTS, - []string{ + GROUP_EVENTS, + []string{ env.String("TOPIC_RAW_WEB"), env.String("TOPIC_RAW_IOS"), - }, + }, func(sessionID uint64, msg messages.Message, meta *types.Meta) { statsLogger.HandleAndLog(sessionID, meta) builderMap.HandleMessage(sessionID, msg, msg.Meta().Index) }, + false, ) - consumer.DisableAutoCommit() - + tick := time.Tick(intervals.EVENTS_COMMIT_INTERVAL * time.Millisecond) sigchan := make(chan os.Signal, 1) - signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) - log.Printf("Ender service started\n") + log.Printf("Ender service started\n") for { select { case sig := <-sigchan: @@ -55,7 +55,7 @@ func main() { consumer.CommitBack(intervals.EVENTS_BACK_COMMIT_GAP) consumer.Close() os.Exit(0) - case <- tick: + case <-tick: builderMap.IterateReadyMessages(time.Now().UnixNano()/1e6, func(sessionID uint64, readyMsg messages.Message) { producer.Produce(TOPIC_TRIGGER, sessionID, messages.Encode(readyMsg)) }) @@ -69,4 +69,3 @@ func main() { } } } - diff --git a/backend/services/sink/main.go b/backend/services/sink/main.go index 5893e93e6..a649bb6ef 100644 --- a/backend/services/sink/main.go +++ b/backend/services/sink/main.go @@ -1,8 +1,8 @@ package main import ( - "log" "encoding/binary" + "log" "time" "os" @@ -10,67 +10,64 @@ import ( "syscall" "openreplay/backend/pkg/env" + . "openreplay/backend/pkg/messages" "openreplay/backend/pkg/queue" "openreplay/backend/pkg/queue/types" - . "openreplay/backend/pkg/messages" ) - - func main() { log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile) - FS_DIR := env.String("FS_DIR"); + FS_DIR := env.String("FS_DIR") if _, err := os.Stat(FS_DIR); os.IsNotExist(err) { log.Fatalf("%v doesn't exist. %v", FS_DIR, err) } writer := NewWriter(env.Uint16("FS_ULIMIT"), FS_DIR) - count := 0 + count := 0 consumer := queue.NewMessageConsumer( env.String("GROUP_SINK"), - []string{ + []string{ env.String("TOPIC_RAW_WEB"), env.String("TOPIC_RAW_IOS"), - }, - func(sessionID uint64, message Message, _ *types.Meta) { - //typeID, err := GetMessageTypeID(value) - // if err != nil { - // log.Printf("Message type decoding error: %v", err) - // return - // } - typeID := message.Meta().TypeID - if !IsReplayerType(typeID) { - return - } + }, + func(sessionID uint64, message Message, _ *types.Meta) { + //typeID, err := GetMessageTypeID(value) + // if err != nil { + // log.Printf("Message type decoding error: %v", err) + // return + // } + typeID := message.Meta().TypeID + if !IsReplayerType(typeID) { + return + } - count++ + count++ - value := message.Encode() - var data []byte - if IsIOSType(typeID) { - data = value - } else { + value := message.Encode() + var data []byte + if IsIOSType(typeID) { + data = value + } else { data = make([]byte, len(value)+8) copy(data[8:], value[:]) binary.LittleEndian.PutUint64(data[0:], message.Meta().Index) - } - if err := writer.Write(sessionID, data); err != nil { + } + if err := writer.Write(sessionID, data); err != nil { log.Printf("Writer error: %v\n", err) } - }, + }, + false, ) - consumer.DisableAutoCommit() - sigchan := make(chan os.Signal, 1) - signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) - tick := time.Tick(30 * time.Second) + tick := time.Tick(30 * time.Second) - log.Printf("Sink service started\n") + log.Printf("Sink service started\n") for { select { case sig := <-sigchan: @@ -85,7 +82,7 @@ func main() { log.Printf("%v messages during 30 sec", count) count = 0 - + consumer.Commit() default: err := consumer.ConsumeNext() @@ -96,4 +93,3 @@ func main() { } } - diff --git a/backend/services/storage/main.go b/backend/services/storage/main.go index 5033fb845..cd585f10e 100644 --- a/backend/services/storage/main.go +++ b/backend/services/storage/main.go @@ -2,45 +2,41 @@ package main import ( "log" - "time" "os" "strconv" + "time" "os/signal" "syscall" "openreplay/backend/pkg/env" - "openreplay/backend/pkg/storage" "openreplay/backend/pkg/messages" "openreplay/backend/pkg/queue" "openreplay/backend/pkg/queue/types" + "openreplay/backend/pkg/storage" ) - - func main() { log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile) - - storageWeb := storage.NewS3(env.String("AWS_REGION_WEB"), env.String("S3_BUCKET_WEB")) - //storageIos := storage.NewS3(env.String("AWS_REGION_IOS"), env.String("S3_BUCKET_IOS")) + storage := storage.NewS3(env.String("AWS_REGION_WEB"), env.String("S3_BUCKET_WEB")) FS_DIR := env.String("FS_DIR") FS_CLEAN_HRS := env.Int("FS_CLEAN_HRS") - var uploadKey func(string, int, *storage.S3) - uploadKey = func(key string, retryCount int, s *storage.S3) { + var uploadKey func(string, int) + uploadKey = func(key string, retryCount int) { if retryCount <= 0 { - return; + return } file, err := os.Open(FS_DIR + "/" + key) defer file.Close() if err != nil { log.Printf("File error: %v; Will retry %v more time(s)\n", err, retryCount) time.AfterFunc(2*time.Minute, func() { - uploadKey(key, retryCount - 1, s) + uploadKey(key, retryCount-1) }) } else { - if err := s.Upload(gzipFile(file), key, "application/octet-stream", true); err != nil { + if err := storage.Upload(gzipFile(file), key, "application/octet-stream", true); err != nil { log.Fatalf("Storage upload error: %v\n", err) } } @@ -48,27 +44,24 @@ func main() { consumer := queue.NewMessageConsumer( env.String("GROUP_STORAGE"), - []string{ + []string{ env.String("TOPIC_TRIGGER"), - }, - func(sessionID uint64, msg messages.Message, meta *types.Meta) { - switch msg.(type) { - case *messages.SessionEnd: - uploadKey(strconv.FormatUint(sessionID, 10), 5, storageWeb) - //case *messages.IOSSessionEnd: - // uploadKey(strconv.FormatUint(sessionID, 10), 5, storageIos) - } - }, + }, + func(sessionID uint64, msg messages.Message, meta *types.Meta) { + switch msg.(type) { + case *messages.SessionEnd: + uploadKey(strconv.FormatUint(sessionID, 10), 5) + } + }, + true, ) sigchan := make(chan os.Signal, 1) - signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM) + cleanTick := time.Tick(time.Duration(FS_CLEAN_HRS) * time.Hour) - cleanTick := time.Tick(time.Duration(FS_CLEAN_HRS) * time.Hour) - - - log.Printf("Storage service started\n") + log.Printf("Storage service started\n") for { select { case sig := <-sigchan: @@ -85,4 +78,3 @@ func main() { } } } - diff --git a/ee/backend/pkg/kafka/consumer.go b/ee/backend/pkg/kafka/consumer.go index ca0544923..82aa56d50 100644 --- a/ee/backend/pkg/kafka/consumer.go +++ b/ee/backend/pkg/kafka/consumer.go @@ -25,7 +25,12 @@ type Consumer struct { lastKafkaEventTs int64 } -func NewConsumer(group string, topics []string, messageHandler types.MessageHandler) *Consumer { +func NewConsumer( + group string, + topics []string, + messageHandler types.MessageHandler, + autoCommit bool, +) *Consumer { protocol := "plaintext" if env.Bool("KAFKA_USE_SSL") { protocol = "ssl" @@ -53,18 +58,19 @@ func NewConsumer(group string, topics []string, messageHandler types.MessageHand log.Fatalln(err) } + var commitTicker *time.Ticker + if autoCommit { + commitTicker = time.NewTicker(2 * time.Minute) + } + return &Consumer{ c: c, messageHandler: messageHandler, - commitTicker: time.NewTicker(2 * time.Minute), + commitTicker: commitTicker, pollTimeout: 200, } } -func (consumer *Consumer) DisableAutoCommit() { - consumer.commitTicker.Stop() -} - func (consumer *Consumer) Commit() error { consumer.c.Commit() // TODO: return error if it is not "No offset stored" return nil @@ -128,10 +134,12 @@ func (consumer *Consumer) ConsumeNext() error { return nil } - select { - case <-consumer.commitTicker.C: - consumer.Commit() - default: + if consumer.commitTicker != nil { + select { + case <-consumer.commitTicker.C: + consumer.Commit() + default: + } } switch e := ev.(type) { diff --git a/ee/backend/pkg/queue/import.go b/ee/backend/pkg/queue/import.go index abff07e9a..e95eb11e5 100644 --- a/ee/backend/pkg/queue/import.go +++ b/ee/backend/pkg/queue/import.go @@ -2,17 +2,16 @@ package queue import ( "openreplay/backend/pkg/kafka" - "openreplay/backend/pkg/queue/types" "openreplay/backend/pkg/license" + "openreplay/backend/pkg/queue/types" ) -func NewConsumer(group string, topics []string, handler types.MessageHandler) types.Consumer { +func NewConsumer(group string, topics []string, handler types.MessageHandler, autoCommit bool) types.Consumer { license.CheckLicense() - return kafka.NewConsumer(group, topics, handler) + return kafka.NewConsumer(group, topics, handler, autoCommit) } func NewProducer() types.Producer { license.CheckLicense() return kafka.NewProducer() } - From daa5a7fad5ca94eb9041570b241a81666944dfd0 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Wed, 30 Mar 2022 18:30:33 +0200 Subject: [PATCH 024/294] codefix(backend): code-format & Close() body on /i --- backend/services/http/handlers_web.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/backend/services/http/handlers_web.go b/backend/services/http/handlers_web.go index 09d2511d8..6020c3eb1 100644 --- a/backend/services/http/handlers_web.go +++ b/backend/services/http/handlers_web.go @@ -11,8 +11,8 @@ import ( "time" "openreplay/backend/pkg/db/postgres" - "openreplay/backend/pkg/token" . "openreplay/backend/pkg/messages" + "openreplay/backend/pkg/token" ) func startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) { @@ -30,12 +30,12 @@ func startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) { UserID string `json:"userID"` } type response struct { - Timestamp int64 `json:"timestamp"` - Delay int64 `json:"delay"` - Token string `json:"token"` - UserUUID string `json:"userUUID"` - SessionID string `json:"sessionID"` - BeaconSizeLimit int64 `json:"beaconSizeLimit"` + Timestamp int64 `json:"timestamp"` + Delay int64 `json:"delay"` + Token string `json:"token"` + UserUUID string `json:"userUUID"` + SessionID string `json:"sessionID"` + BeaconSizeLimit int64 `json:"beaconSizeLimit"` } startTime := time.Now() @@ -102,7 +102,7 @@ func startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) { UserCountry: country, UserDeviceMemorySize: req.DeviceMemory, UserDeviceHeapSize: req.JsHeapSizeLimit, - UserID: req.UserID, + UserID: req.UserID, })) } @@ -110,9 +110,9 @@ func startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) { responseWithJSON(w, &response{ //Timestamp: startTime.UnixNano() / 1e6, //Delay: delayDuration.Nanoseconds() / 1e6, - Token: tokenizer.Compose(*tokenData), - UserUUID: userUUID, - SessionID: strconv.FormatUint(tokenData.ID, 10), + Token: tokenizer.Compose(*tokenData), + UserUUID: userUUID, + SessionID: strconv.FormatUint(tokenData.ID, 10), BeaconSizeLimit: BEACON_SIZE_LIMIT, }) } @@ -124,7 +124,7 @@ func pushMessagesHandlerWeb(w http.ResponseWriter, r *http.Request) { return } body := http.MaxBytesReader(w, r.Body, BEACON_SIZE_LIMIT) - //defer body.Close() + defer body.Close() buf, err := ioutil.ReadAll(body) if err != nil { responseWithError(w, http.StatusInternalServerError, err) // TODO: send error here only on staging @@ -248,4 +248,4 @@ func notStartedHandlerWeb(w http.ResponseWriter, r *http.Request) { log.Printf("Unable to insert Unstarted Session: %v\n", err) } w.WriteHeader(http.StatusOK) -} \ No newline at end of file +} From 17436657691391eddd6d9bd8f0b04674ce13d99a Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 30 Mar 2022 18:38:48 +0200 Subject: [PATCH 025/294] 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 026/294] 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 027/294] 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 028/294] 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 029/294] 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 9e294d4edd6f52a67eab190e3f3772af7b0d460a Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 31 Mar 2022 18:32:27 +0200 Subject: [PATCH 030/294] fix(nginx): socketio endpoint directly from kube service nginx upstream block has some issue with forwarding the socketio connection Signed-off-by: rjshrjndrn --- .../openreplay/charts/nginx-ingress/templates/configMap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/configMap.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/configMap.yaml index 6780c9ee6..6322318e3 100644 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/configMap.yaml +++ b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/configMap.yaml @@ -82,7 +82,7 @@ data: proxy_set_header Host $host; proxy_set_header X-Forwarded-For $origin_forwarded_ip; proxy_set_header X-Real-IP $origin_forwarded_ip; - proxy_pass http://utilities-pool; + proxy_pass http://utilities-openreplay.app.svc.cluster.local:9001; } location /assets/ { rewrite ^/assets/(.*) /sessions-assets/$1 break; From 77fc8221ac6b49631ff92b8f7cc75558f917cd81 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 31 Mar 2022 19:50:07 +0200 Subject: [PATCH 031/294] chore(helm): moving utils default vars Signed-off-by: rjshrjndrn --- scripts/helmcharts/openreplay/charts/utilities/values.yaml | 3 +++ scripts/helmcharts/vars.yaml | 7 ------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/scripts/helmcharts/openreplay/charts/utilities/values.yaml b/scripts/helmcharts/openreplay/charts/utilities/values.yaml index 87b3d771d..8265b5aa5 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/values.yaml +++ b/scripts/helmcharts/openreplay/charts/utilities/values.yaml @@ -83,6 +83,9 @@ autoscaling: env: REDIS_URL: "redis://redis-master.db.svc.cluster.local:6379" + debug: 0 + uws: false + redis: false nodeSelector: {} diff --git a/scripts/helmcharts/vars.yaml b/scripts/helmcharts/vars.yaml index e4ed88711..24ea9344d 100644 --- a/scripts/helmcharts/vars.yaml +++ b/scripts/helmcharts/vars.yaml @@ -95,13 +95,6 @@ chalice: # idp_name: '' # idp_tenantKey: '' -utilities: - replicaCount: 1 - env: - debug: 0 - uws: false - redis: false - # If you want to override something # chartname: # filedFrom chart/Values.yaml: From cfcff0293aeca227cded754ae3a18ffd2528dd79 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 1 Apr 2022 15:26:24 +0200 Subject: [PATCH 032/294] 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 a0a82e0d77a75d6154ea6fffc5e58128880fa117 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 1 Apr 2022 17:19:46 +0200 Subject: [PATCH 033/294] codeclean(backend): remove alert.go --- backend/pkg/db/postgres/alert.go | 228 ------------------------------- 1 file changed, 228 deletions(-) delete mode 100644 backend/pkg/db/postgres/alert.go diff --git a/backend/pkg/db/postgres/alert.go b/backend/pkg/db/postgres/alert.go deleted file mode 100644 index 964977bd3..000000000 --- a/backend/pkg/db/postgres/alert.go +++ /dev/null @@ -1,228 +0,0 @@ -package postgres - -import ( - "database/sql" - "errors" - "fmt" - sq "github.com/Masterminds/squirrel" - "log" - "strconv" - "time" -) - -type TimeString sql.NullString -type query struct { - Left string `db:"query.left" json:"left"` - Operator string `db:"query.operator" json:"operator"` - Right float64 `db:"query.right" json:"right"` -} -type options struct { - RenotifyInterval int64 `db:"options.renotifyInterval" json:"renotifyInterval"` - LastNotification int64 `db:"options.lastNotification" json:"lastNotification;omitempty"` - CurrentPeriod int64 `db:"options.currentPeriod" json:"currentPeriod"` - PreviousPeriod int64 `db:"options.previousPeriod" json:"previousPeriod;omitempty"` - Message []map[string]string `db:"options.message" json:"message;omitempty"` - Change string `db:"options.change" json:"change;omitempty"` -} -type Alert struct { - AlertID uint32 `db:"alert_id" json:"alert_id"` - ProjectID uint32 `db:"project_id" json:"project_id"` - Name string `db:"name" json:"name"` - Description sql.NullString `db:"description" json:"description"` - Active bool `db:"active" json:"active"` - DetectionMethod string `db:"detection_method" json:"detection_method"` - Query query `db:"query" json:"query"` - DeletedAt *int64 `db:"deleted_at" json:"deleted_at"` - CreatedAt *int64 `db:"created_at" json:"created_at"` - Options options `db:"options" json:"options"` - TenantId uint32 `db:"tenant_id" json:"tenant_id"` -} - -func (pg *Conn) IterateAlerts(iter func(alert *Alert, err error)) error { - rows, err := pg.query(` - SELECT - alerts.alert_id, - alerts.project_id, - alerts.name, - alerts.description, - alerts.active, - alerts.detection_method, - alerts.query, - CAST(EXTRACT(epoch FROM alerts.deleted_at) * 1000 AS BIGINT) AS deleted_at, - CAST(EXTRACT(epoch FROM alerts.created_at) * 1000 AS BIGINT) AS created_at, - alerts.options, - 0 AS tenant_id - FROM public.alerts - WHERE alerts.active AND alerts.deleted_at ISNULL; - `) - if err != nil { - return err - } - defer rows.Close() - for rows.Next() { - a := new(Alert) - if err = rows.Scan( - &a.AlertID, - &a.ProjectID, - &a.Name, - &a.Description, - &a.Active, - &a.DetectionMethod, - &a.Query, - &a.DeletedAt, - &a.CreatedAt, - &a.Options, - &a.TenantId, - ); err != nil { - iter(nil, err) - continue - } - iter(a, nil) - } - - if err = rows.Err(); err != nil { - return err - } - return nil -} - -func (pg *Conn) SaveLastNotification(allIds []uint32) error { - var paramrefs string - for _, v := range allIds { - paramrefs += strconv.Itoa(int(v)) + `,` - } - paramrefs = paramrefs[:len(paramrefs)-1] // remove last "," - q := "UPDATE public.Alerts SET options = options||'{\"lastNotification\":" + strconv.Itoa(int(time.Now().Unix()*1000)) + "}'::jsonb WHERE alert_id IN (" + paramrefs + ");" - //log.Println(q) - log.Println("Updating PG") - return pg.exec(q) -} - -type columnDefinition struct { - table string - formula string - condition string - group string -} - -var LeftToDb = map[string]columnDefinition{ - "performance.dom_content_loaded.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "COALESCE(AVG(NULLIF(dom_content_loaded_time ,0)),0)"}, - "performance.first_meaningful_paint.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "COALESCE(AVG(NULLIF(first_contentful_paint_time,0)),0)"}, - "performance.page_load_time.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(load_time ,0))"}, - "performance.dom_build_time.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(dom_building_time,0))"}, - "performance.speed_index.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(speed_index,0))"}, - "performance.page_response_time.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(response_time,0))"}, - "performance.ttfb.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(first_paint_time,0))"}, - "performance.time_to_render.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(visually_complete,0))"}, - "performance.image_load_time.average": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(resources.duration,0))", condition: "type='img'"}, - "performance.request_load_time.average": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(resources.duration,0))", condition: "type='fetch'"}, - "resources.load_time.average": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(resources.duration,0))"}, - "resources.missing.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(DISTINCT url_hostpath)", condition: "success= FALSE"}, - "errors.4xx_5xx.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(session_id)", condition: "status/100!=2"}, - "errors.4xx.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(session_id)", condition: "status/100=4"}, - "errors.5xx.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(session_id)", condition: "status/100=5"}, - "errors.javascript.impacted_sessions.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(DISTINCT session_id)", condition: "success= FALSE AND type='script'"}, - "performance.crashes.count": {table: "(SELECT *, start_ts AS timestamp FROM public.sessions WHERE errors_count > 0) AS sessions", formula: "COUNT(DISTINCT session_id)", condition: "errors_count > 0"}, - "errors.javascript.count": {table: "events.errors INNER JOIN public.errors AS m_errors USING (error_id)", formula: "COUNT(DISTINCT session_id)", condition: "source='js_exception'"}, - "errors.backend.count": {table: "events.errors INNER JOIN public.errors AS m_errors USING (error_id)", formula: "COUNT(DISTINCT session_id)", condition: "source!='js_exception'"}, -} - -//This is the frequency of execution for each threshold -var TimeInterval = map[int64]int64{ - 15: 3, - 30: 5, - 60: 10, - 120: 20, - 240: 30, - 1440: 60, -} - -func (a *Alert) CanCheck() bool { - now := time.Now().Unix() * 1000 - var repetitionBase int64 - - if repetitionBase = a.Options.CurrentPeriod; a.DetectionMethod == "change" && a.Options.CurrentPeriod > a.Options.PreviousPeriod { - repetitionBase = a.Options.PreviousPeriod - } - - if _, ok := TimeInterval[repetitionBase]; !ok { - log.Printf("repetitionBase: %d NOT FOUND", repetitionBase) - return false - } - return a.DeletedAt == nil && a.Active && - (a.Options.RenotifyInterval <= 0 || - a.Options.LastNotification <= 0 || - ((now - a.Options.LastNotification) > a.Options.RenotifyInterval*60*1000)) && - ((now-*a.CreatedAt)%(TimeInterval[repetitionBase]*60*1000)) < 60*1000 -} - -func (a *Alert) Build() (sq.SelectBuilder, error) { - colDef, ok := LeftToDb[a.Query.Left] - if !ok { - return sq.Select(), errors.New(fmt.Sprintf("!! unsupported metric '%s' from alert: %d:%s\n", a.Query.Left, a.AlertID, a.Name)) - } - - subQ := sq. - Select(colDef.formula + " AS value"). - From(colDef.table). - Where(sq.And{sq.Expr("project_id = $1 ", a.ProjectID), - sq.Expr(colDef.condition)}) - q := sq.Select(fmt.Sprint("value, coalesce(value,0)", a.Query.Operator, a.Query.Right, " AS valid")) - if len(colDef.group) > 0 { - subQ = subQ.Column(colDef.group + " AS group_value") - subQ = subQ.GroupBy(colDef.group) - q = q.Column("group_value") - } - - if a.DetectionMethod == "threshold" { - q = q.FromSelect(subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)), "stat") - } else if a.DetectionMethod == "change" { - if a.Options.Change == "change" { - if len(colDef.group) == 0 { - sub1, args1, _ := subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)).ToSql() - sub2, args2, _ := subQ.Where( - sq.And{ - sq.Expr("timestamp<$3 ", time.Now().Unix()-a.Options.CurrentPeriod*60), - sq.Expr("timestamp>=$4 ", time.Now().Unix()-2*a.Options.CurrentPeriod*60), - }).ToSql() - sub1, _, _ = sq.Expr("SELECT ((" + sub1 + ")-(" + sub2 + ")) AS value").ToSql() - q = q.JoinClause("FROM ("+sub1+") AS stat", append(args1, args2...)...) - } else { - subq1 := subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)) - sub2, args2, _ := subQ.Where( - sq.And{ - sq.Expr("timestamp<$3 ", time.Now().Unix()-a.Options.CurrentPeriod*60), - sq.Expr("timestamp>=$4 ", time.Now().Unix()-2*a.Options.CurrentPeriod*60), - }).ToSql() - sub1 := sq.Select("group_value", "(stat1.value-stat2.value) AS value").FromSelect(subq1, "stat1").JoinClause("INNER JOIN ("+sub2+") AS stat2 USING(group_value)", args2...) - q = q.FromSelect(sub1, "stat") - } - } else if a.Options.Change == "percent" { - if len(colDef.group) == 0 { - sub1, args1, _ := subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)).ToSql() - sub2, args2, _ := subQ.Where( - sq.And{ - sq.Expr("timestamp<$3 ", time.Now().Unix()-a.Options.CurrentPeriod*60), - sq.Expr("timestamp>=$4 ", time.Now().Unix()-a.Options.PreviousPeriod*60-a.Options.CurrentPeriod*60), - }).ToSql() - sub1, _, _ = sq.Expr("SELECT ((" + sub1 + ")/(" + sub2 + ")-1)*100 AS value").ToSql() - q = q.JoinClause("FROM ("+sub1+") AS stat", append(args1, args2...)...) - } else { - subq1 := subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)) - sub2, args2, _ := subQ.Where( - sq.And{ - sq.Expr("timestamp<$3 ", time.Now().Unix()-a.Options.CurrentPeriod*60), - sq.Expr("timestamp>=$4 ", time.Now().Unix()-a.Options.PreviousPeriod*60-a.Options.CurrentPeriod*60), - }).ToSql() - sub1 := sq.Select("group_value", "(stat1.value/stat2.value-1)*100 AS value").FromSelect(subq1, "stat1").JoinClause("INNER JOIN ("+sub2+") AS stat2 USING(group_value)", args2...) - q = q.FromSelect(sub1, "stat") - } - } else { - return q, errors.New("unsupported change method") - } - - } else { - return q, errors.New("unsupported detection method") - } - return q, nil -} \ No newline at end of file From 197c39f96ac83200f67d0ff40db86a93373b19eb Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 1 Apr 2022 19:14:08 +0200 Subject: [PATCH 034/294] feat(backend): go v.1.18 upgrade --- backend/Dockerfile | 2 +- backend/go.mod | 43 +++++- backend/go.sum | 21 +-- ...{messages_common.go => messages-common.go} | 24 --- .../{messages_ios.go => messages-ios.go} | 0 .../{messages_web.go => messages-web.go} | 0 .../pkg/db/cache/{pg_cache.go => pg-cache.go} | 0 backend/pkg/db/postgres/listener.go | 25 ---- ...{messages_common.go => messages-common.go} | 0 .../{messages_ios.go => messages-ios.go} | 0 ...ges_web_stats.go => messages-web-stats.go} | 0 .../{messages_web.go => messages-web.go} | 0 ...tarted_session.go => unstarted-session.go} | 0 .../pkg/env/{worker_id.go => worker-id.go} | 0 .../{get_timestamp.go => get-timestamp.go} | 0 ...ansform.go => legacy-message-transform.go} | 0 .../{read_message.go => read-message.go} | 0 ...s_depricated.go => handlers-depricated.go} | 0 .../http/{handlers_ios.go => handlers-ios.go} | 98 ++++++------- .../http/{handlers_web.go => handlers-web.go} | 0 backend/services/http/ios-device.go | 138 ++++++++++++++++++ backend/services/http/ios_device.go | 79 ---------- backend/services/http/main.go | 18 +-- backend/services/http/project_id.go | 12 -- 24 files changed, 235 insertions(+), 225 deletions(-) rename backend/pkg/db/cache/{messages_common.go => messages-common.go} (66%) rename backend/pkg/db/cache/{messages_ios.go => messages-ios.go} (100%) rename backend/pkg/db/cache/{messages_web.go => messages-web.go} (100%) rename backend/pkg/db/cache/{pg_cache.go => pg-cache.go} (100%) rename backend/pkg/db/postgres/{messages_common.go => messages-common.go} (100%) rename backend/pkg/db/postgres/{messages_ios.go => messages-ios.go} (100%) rename backend/pkg/db/postgres/{messages_web_stats.go => messages-web-stats.go} (100%) rename backend/pkg/db/postgres/{messages_web.go => messages-web.go} (100%) rename backend/pkg/db/postgres/{unstarted_session.go => unstarted-session.go} (100%) rename backend/pkg/env/{worker_id.go => worker-id.go} (100%) rename backend/pkg/messages/{get_timestamp.go => get-timestamp.go} (100%) rename backend/pkg/messages/{legacy_message_transform.go => legacy-message-transform.go} (100%) rename backend/pkg/messages/{read_message.go => read-message.go} (100%) rename backend/services/http/{handlers_depricated.go => handlers-depricated.go} (100%) rename backend/services/http/{handlers_ios.go => handlers-ios.go} (71%) rename backend/services/http/{handlers_web.go => handlers-web.go} (100%) create mode 100644 backend/services/http/ios-device.go delete mode 100644 backend/services/http/ios_device.go delete mode 100644 backend/services/http/project_id.go diff --git a/backend/Dockerfile b/backend/Dockerfile index 5cefd4cb4..4c31518ef 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.13-alpine3.10 AS prepare +FROM golang:1.18-alpine3.15 AS prepare RUN apk add --no-cache git openssh openssl-dev pkgconf gcc g++ make libc-dev bash diff --git a/backend/go.mod b/backend/go.mod index ab98ca444..6588529a8 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,14 +1,12 @@ module openreplay/backend -go 1.13 +go 1.18 require ( cloud.google.com/go/logging v1.4.2 github.com/ClickHouse/clickhouse-go v1.4.3 - github.com/Masterminds/squirrel v1.5.0 github.com/aws/aws-sdk-go v1.35.23 github.com/btcsuite/btcutil v1.0.2 - github.com/confluentinc/confluent-kafka-go v1.7.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 github.com/go-redis/redis v6.15.9+incompatible github.com/google/uuid v1.1.2 @@ -16,14 +14,47 @@ require ( github.com/jackc/pgconn v1.6.0 github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451 github.com/jackc/pgx/v4 v4.6.0 - github.com/klauspost/compress v1.11.9 // indirect github.com/klauspost/pgzip v1.2.5 - github.com/lib/pq v1.2.0 github.com/oschwald/maxminddb-golang v1.7.0 github.com/pkg/errors v0.9.1 github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce github.com/ua-parser/uap-go v0.0.0-20200325213135-e1c09f13e2fe + golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 google.golang.org/api v0.50.0 gopkg.in/confluentinc/confluent-kafka-go.v1 v1.7.0 - +) + +require ( + cloud.google.com/go v0.84.0 // indirect + github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect + github.com/confluentinc/confluent-kafka-go v1.7.0 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.6 // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.0.2 // indirect + github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 // indirect + github.com/jackc/pgtype v1.3.0 // indirect + github.com/jackc/puddle v1.1.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/jstemmer/go-junit-report v0.9.1 // indirect + github.com/klauspost/compress v1.11.9 // indirect + go.opencensus.io v0.23.0 // indirect + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect + golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect + golang.org/x/mod v0.4.2 // indirect + golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 // indirect + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect + golang.org/x/text v0.3.6 // indirect + golang.org/x/tools v0.1.4 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84 // indirect + google.golang.org/grpc v1.38.0 // indirect + google.golang.org/protobuf v1.26.0 // indirect + gopkg.in/yaml.v2 v2.2.8 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index 8d538a0b4..607936204 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -46,8 +46,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/clickhouse-go v1.4.3 h1:iAFMa2UrQdR5bHJ2/yaSLffZkxpcOYQMCUuKeNXGdqc= github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= -github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= -github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/aws/aws-sdk-go v1.35.23 h1:SCP0d0XvyJTDmfnHEQPvBaYi3kea1VNUo7uQmkVgFts= github.com/aws/aws-sdk-go v1.35.23/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= @@ -75,8 +73,8 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/confluentinc/confluent-kafka-go v1.5.2 h1:l+qt+a0Okmq0Bdr1P55IX4fiwFJyg0lZQmfHkAFkv7E= -github.com/confluentinc/confluent-kafka-go v1.5.2/go.mod h1:u2zNLny2xq+5rWeTQjFHbDzzNuba4P1vo31r9r4uAdg= +github.com/confluentinc/confluent-kafka-go v1.7.0 h1:tXh3LWb2Ne0WiU3ng4h5qiGA9XV61rz46w60O+cq8bM= +github.com/confluentinc/confluent-kafka-go v1.7.0/go.mod h1:u2zNLny2xq+5rWeTQjFHbDzzNuba4P1vo31r9r4uAdg= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= @@ -93,7 +91,6 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -135,7 +132,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -152,11 +148,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -184,7 +178,6 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -203,7 +196,6 @@ github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye47 github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -219,7 +211,6 @@ github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCM github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.3.0 h1:l8JvKrby3RI7Kg3bYEeU9TA4vqC38QDpFCfcrC7KuN0= github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik= -github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o= github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= @@ -254,10 +245,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= @@ -682,8 +669,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/confluentinc/confluent-kafka-go.v1 v1.5.2 h1:g0WBLy6fobNUU8W/e9zx6I0Yl79Ya+BDW1NwzAlTiiQ= -gopkg.in/confluentinc/confluent-kafka-go.v1 v1.5.2/go.mod h1:ZdI3yfYmdNSLQPNCpO1y00EHyWaHG5EnQEyL/ntAegY= +gopkg.in/confluentinc/confluent-kafka-go.v1 v1.7.0 h1:+RlmciBLDd/XwM1iudiG3HtCg45purnsOxEoY/+JZdQ= +gopkg.in/confluentinc/confluent-kafka-go.v1 v1.7.0/go.mod h1:ZdI3yfYmdNSLQPNCpO1y00EHyWaHG5EnQEyL/ntAegY= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/backend/pkg/db/cache/messages_common.go b/backend/pkg/db/cache/messages-common.go similarity index 66% rename from backend/pkg/db/cache/messages_common.go rename to backend/pkg/db/cache/messages-common.go index 3983982fe..8ca7b2f85 100644 --- a/backend/pkg/db/cache/messages_common.go +++ b/backend/pkg/db/cache/messages-common.go @@ -28,30 +28,6 @@ func (c *PGCache) InsertIssueEvent(sessionID uint64, crash *IssueEvent) error { return c.Conn.InsertIssueEvent(sessionID, session.ProjectID, crash) } -func (c *PGCache) InsertUserID(sessionID uint64, userID *IOSUserID) error { - if err := c.Conn.InsertIOSUserID(sessionID, userID); err != nil { - return err - } - session, err := c.GetSession(sessionID) - if err != nil { - return err - } - session.UserID = &userID.Value - return nil -} - -func (c *PGCache) InsertUserAnonymousID(sessionID uint64, userAnonymousID *IOSUserAnonymousID) error { - if err := c.Conn.InsertIOSUserAnonymousID(sessionID, userAnonymousID); err != nil { - return err - } - session, err := c.GetSession(sessionID) - if err != nil { - return err - } - session.UserAnonymousID = &userAnonymousID.Value - return nil -} - func (c *PGCache) InsertMetadata(sessionID uint64, metadata *Metadata) error { session, err := c.GetSession(sessionID) if err != nil { diff --git a/backend/pkg/db/cache/messages_ios.go b/backend/pkg/db/cache/messages-ios.go similarity index 100% rename from backend/pkg/db/cache/messages_ios.go rename to backend/pkg/db/cache/messages-ios.go diff --git a/backend/pkg/db/cache/messages_web.go b/backend/pkg/db/cache/messages-web.go similarity index 100% rename from backend/pkg/db/cache/messages_web.go rename to backend/pkg/db/cache/messages-web.go diff --git a/backend/pkg/db/cache/pg_cache.go b/backend/pkg/db/cache/pg-cache.go similarity index 100% rename from backend/pkg/db/cache/pg_cache.go rename to backend/pkg/db/cache/pg-cache.go diff --git a/backend/pkg/db/postgres/listener.go b/backend/pkg/db/postgres/listener.go index f90d83485..ef99c2c59 100644 --- a/backend/pkg/db/postgres/listener.go +++ b/backend/pkg/db/postgres/listener.go @@ -11,7 +11,6 @@ import ( type Listener struct { conn *pgx.Conn Integrations chan *Integration - Alerts chan *Alert Errors chan error } @@ -32,23 +31,6 @@ func NewIntegrationsListener(url string) (*Listener, error) { return listener, nil } -func NewAlertsListener(url string) (*Listener, error) { - conn, err := pgx.Connect(context.Background(), url) - if err != nil { - return nil, err - } - listener := &Listener{ - conn: conn, - Errors: make(chan error), - } - listener.Alerts = make(chan *Alert, 50) - if _, err := conn.Exec(context.Background(), "LISTEN alert"); err != nil { - return nil, err - } - go listener.listen() - return listener, nil -} - func (listener *Listener) listen() { for { notification, err := listener.conn.WaitForNotification(context.Background()) @@ -64,13 +46,6 @@ func (listener *Listener) listen() { } else { listener.Integrations <- integrationP } - case "alert": - alertP := new(Alert) - if err := json.Unmarshal([]byte(notification.Payload), alertP); err != nil { - listener.Errors <- fmt.Errorf("%v | Payload: %v", err, notification.Payload) - } else { - listener.Alerts <- alertP - } } } } diff --git a/backend/pkg/db/postgres/messages_common.go b/backend/pkg/db/postgres/messages-common.go similarity index 100% rename from backend/pkg/db/postgres/messages_common.go rename to backend/pkg/db/postgres/messages-common.go diff --git a/backend/pkg/db/postgres/messages_ios.go b/backend/pkg/db/postgres/messages-ios.go similarity index 100% rename from backend/pkg/db/postgres/messages_ios.go rename to backend/pkg/db/postgres/messages-ios.go diff --git a/backend/pkg/db/postgres/messages_web_stats.go b/backend/pkg/db/postgres/messages-web-stats.go similarity index 100% rename from backend/pkg/db/postgres/messages_web_stats.go rename to backend/pkg/db/postgres/messages-web-stats.go diff --git a/backend/pkg/db/postgres/messages_web.go b/backend/pkg/db/postgres/messages-web.go similarity index 100% rename from backend/pkg/db/postgres/messages_web.go rename to backend/pkg/db/postgres/messages-web.go diff --git a/backend/pkg/db/postgres/unstarted_session.go b/backend/pkg/db/postgres/unstarted-session.go similarity index 100% rename from backend/pkg/db/postgres/unstarted_session.go rename to backend/pkg/db/postgres/unstarted-session.go diff --git a/backend/pkg/env/worker_id.go b/backend/pkg/env/worker-id.go similarity index 100% rename from backend/pkg/env/worker_id.go rename to backend/pkg/env/worker-id.go diff --git a/backend/pkg/messages/get_timestamp.go b/backend/pkg/messages/get-timestamp.go similarity index 100% rename from backend/pkg/messages/get_timestamp.go rename to backend/pkg/messages/get-timestamp.go diff --git a/backend/pkg/messages/legacy_message_transform.go b/backend/pkg/messages/legacy-message-transform.go similarity index 100% rename from backend/pkg/messages/legacy_message_transform.go rename to backend/pkg/messages/legacy-message-transform.go diff --git a/backend/pkg/messages/read_message.go b/backend/pkg/messages/read-message.go similarity index 100% rename from backend/pkg/messages/read_message.go rename to backend/pkg/messages/read-message.go diff --git a/backend/services/http/handlers_depricated.go b/backend/services/http/handlers-depricated.go similarity index 100% rename from backend/services/http/handlers_depricated.go rename to backend/services/http/handlers-depricated.go diff --git a/backend/services/http/handlers_ios.go b/backend/services/http/handlers-ios.go similarity index 71% rename from backend/services/http/handlers_ios.go rename to backend/services/http/handlers-ios.go index 6c3f945bd..affcab59d 100644 --- a/backend/services/http/handlers_ios.go +++ b/backend/services/http/handlers-ios.go @@ -2,50 +2,50 @@ package main import ( "encoding/json" - "net/http" "errors" - "time" - "math/rand" - "strconv" "log" + "math/rand" + "net/http" + "strconv" + "time" "openreplay/backend/pkg/db/postgres" - "openreplay/backend/pkg/token" . "openreplay/backend/pkg/messages" + "openreplay/backend/pkg/token" ) -const FILES_SIZE_LIMIT int64 = 1e7 // 10Mb +const FILES_SIZE_LIMIT int64 = 1e7 // 10Mb func startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) { type request struct { - Token string `json:"token"` - ProjectKey *string `json:"projectKey"` - TrackerVersion string `json:"trackerVersion"` - RevID string `json:"revID"` - UserUUID *string `json:"userUUID"` + Token string `json:"token"` + ProjectKey *string `json:"projectKey"` + TrackerVersion string `json:"trackerVersion"` + RevID string `json:"revID"` + UserUUID *string `json:"userUUID"` //UserOS string `json"userOS"` //hardcoded 'MacOS' - UserOSVersion string `json:"userOSVersion"` - UserDevice string `json:"userDevice"` - Timestamp uint64 `json:"timestamp"` + UserOSVersion string `json:"userOSVersion"` + UserDevice string `json:"userDevice"` + Timestamp uint64 `json:"timestamp"` // UserDeviceType uint 0:phone 1:pad 2:tv 3:carPlay 5:mac // “performances”:{ - // “activeProcessorCount”:8, - // “isLowPowerModeEnabled”:0, - // “orientation”:0, - // “systemUptime”:585430, - // “batteryState”:0, - // “thermalState”:0, - // “batteryLevel”:0, - // “processorCount”:8, - // “physicalMemory”:17179869184 - // }, + // “activeProcessorCount”:8, + // “isLowPowerModeEnabled”:0, + // “orientation”:0, + // “systemUptime”:585430, + // “batteryState”:0, + // “thermalState”:0, + // “batteryLevel”:0, + // “processorCount”:8, + // “physicalMemory”:17179869184 + // }, } type response struct { - Token string `json:"token"` - ImagesHashList []string `json:"imagesHashList"` - UserUUID string `json:"userUUID"` - BeaconSizeLimit int64 `json:"beaconSizeLimit"` - SessionID string `json:"sessionID"` + Token string `json:"token"` + ImagesHashList []string `json:"imagesHashList"` + UserUUID string `json:"userUUID"` + BeaconSizeLimit int64 `json:"beaconSizeLimit"` + SessionID string `json:"sessionID"` } startTime := time.Now() req := &request{} @@ -98,16 +98,16 @@ func startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) { // The difference with web is mostly here: producer.Produce(TOPIC_RAW_IOS, tokenData.ID, Encode(&IOSSessionStart{ - Timestamp: req.Timestamp, - ProjectID: uint64(p.ProjectID), - TrackerVersion: req.TrackerVersion, - RevID: req.RevID, - UserUUID: userUUID, - UserOS: "IOS", - UserOSVersion: req.UserOSVersion, - UserDevice: MapIOSDevice(req.UserDevice), - UserDeviceType: GetIOSDeviceType(req.UserDevice), - UserCountry: country, + Timestamp: req.Timestamp, + ProjectID: uint64(p.ProjectID), + TrackerVersion: req.TrackerVersion, + RevID: req.RevID, + UserUUID: userUUID, + UserOS: "IOS", + UserOSVersion: req.UserOSVersion, + UserDevice: MapIOSDevice(req.UserDevice), + UserDeviceType: GetIOSDeviceType(req.UserDevice), + UserCountry: country, })) } @@ -119,14 +119,13 @@ func startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) { responseWithJSON(w, &response{ // ImagesHashList: imagesHashList, - Token: tokenizer.Compose(*tokenData), - UserUUID: userUUID, - SessionID: strconv.FormatUint(tokenData.ID, 10), + Token: tokenizer.Compose(*tokenData), + UserUUID: userUUID, + SessionID: strconv.FormatUint(tokenData.ID, 10), BeaconSizeLimit: BEACON_SIZE_LIMIT, }) } - func pushMessagesHandlerIOS(w http.ResponseWriter, r *http.Request) { sessionData, err := tokenizer.ParseFromHTTPRequest(r) if err != nil { @@ -136,8 +135,6 @@ func pushMessagesHandlerIOS(w http.ResponseWriter, r *http.Request) { pushMessages(w, r, sessionData.ID, TOPIC_RAW_IOS) } - - func pushLateMessagesHandlerIOS(w http.ResponseWriter, r *http.Request) { sessionData, err := tokenizer.ParseFromHTTPRequest(r) if err != nil && err != token.EXPIRED { @@ -145,10 +142,9 @@ func pushLateMessagesHandlerIOS(w http.ResponseWriter, r *http.Request) { return } // Check timestamps here? - pushMessages(w, r, sessionData.ID,TOPIC_RAW_IOS) + pushMessages(w, r, sessionData.ID, TOPIC_RAW_IOS) } - func imagesUploadHandlerIOS(w http.ResponseWriter, r *http.Request) { log.Printf("recieved imagerequest") @@ -163,12 +159,12 @@ func imagesUploadHandlerIOS(w http.ResponseWriter, r *http.Request) { err = r.ParseMultipartForm(1e6) // ~1Mb if err == http.ErrNotMultipart || err == http.ErrMissingBoundary { responseWithError(w, http.StatusUnsupportedMediaType, err) - // } else if err == multipart.ErrMessageTooLarge // if non-files part exceeds 10 MB + // } else if err == multipart.ErrMessageTooLarge // if non-files part exceeds 10 MB } else if err != nil { responseWithError(w, http.StatusInternalServerError, err) // TODO: send error here only on staging } - if (r.MultipartForm == nil) { + if r.MultipartForm == nil { responseWithError(w, http.StatusInternalServerError, errors.New("Multipart not parsed")) } @@ -177,7 +173,7 @@ func imagesUploadHandlerIOS(w http.ResponseWriter, r *http.Request) { return } - prefix := r.MultipartForm.Value["projectKey"][0] + "/" + strconv.FormatUint(sessionData.ID, 10) + "/" + prefix := r.MultipartForm.Value["projectKey"][0] + "/" + strconv.FormatUint(sessionData.ID, 10) + "/" for _, fileHeaderList := range r.MultipartForm.File { for _, fileHeader := range fileHeaderList { @@ -187,7 +183,7 @@ func imagesUploadHandlerIOS(w http.ResponseWriter, r *http.Request) { } key := prefix + fileHeader.Filename log.Printf("Uploading image... %v", key) - go func() { //TODO: mime type from header + go func() { //TODO: mime type from header if err := s3.Upload(file, key, "image/jpeg", false); err != nil { log.Printf("Upload ios screen error. %v", err) } diff --git a/backend/services/http/handlers_web.go b/backend/services/http/handlers-web.go similarity index 100% rename from backend/services/http/handlers_web.go rename to backend/services/http/handlers-web.go diff --git a/backend/services/http/ios-device.go b/backend/services/http/ios-device.go new file mode 100644 index 000000000..bec1f3b36 --- /dev/null +++ b/backend/services/http/ios-device.go @@ -0,0 +1,138 @@ +package main + +import ( + "strings" +) + +func MapIOSDevice(identifier string) string { + switch identifier { + case "iPod5,1": + return "iPod touch (5th generation)" + case "iPod7,1": + return "iPod touch (6th generation)" + case "iPod9,1": + return "iPod touch (7th generation)" + case "iPhone3,1", "iPhone3,2", "iPhone3,3": + return "iPhone 4" + case "iPhone4,1": + return "iPhone 4s" + case "iPhone5,1", "iPhone5,2": + return "iPhone 5" + case "iPhone5,3", "iPhone5,4": + return "iPhone 5c" + case "iPhone6,1", "iPhone6,2": + return "iPhone 5s" + case "iPhone7,2": + return "iPhone 6" + case "iPhone7,1": + return "iPhone 6 Plus" + case "iPhone8,1": + return "iPhone 6s" + case "iPhone8,2": + return "iPhone 6s Plus" + case "iPhone8,4": + return "iPhone SE" + case "iPhone9,1", "iPhone9,3": + return "iPhone 7" + case "iPhone9,2", "iPhone9,4": + return "iPhone 7 Plus" + case "iPhone10,1", "iPhone10,4": + return "iPhone 8" + case "iPhone10,2", "iPhone10,5": + return "iPhone 8 Plus" + case "iPhone10,3", "iPhone10,6": + return "iPhone X" + case "iPhone11,2": + return "iPhone XS" + case "iPhone11,4", "iPhone11,6": + return "iPhone XS Max" + case "iPhone11,8": + return "iPhone XR" + case "iPhone12,1": + return "iPhone 11" + case "iPhone12,3": + return "iPhone 11 Pro" + case "iPhone12,5": + return "iPhone 11 Pro Max" + case "iPhone12,8": + return "iPhone SE (2nd generation)" + case "iPhone13,1": + return "iPhone 12 mini" + case "iPhone13,2": + return "iPhone 12" + case "iPhone13,3": + return "iPhone 12 Pro" + case "iPhone13,4": + return "iPhone 12 Pro Max" + case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": + return "iPad 2" + case "iPad3,1", "iPad3,2", "iPad3,3": + return "iPad (3rd generation)" + case "iPad3,4", "iPad3,5", "iPad3,6": + return "iPad (4th generation)" + case "iPad6,11", "iPad6,12": + return "iPad (5th generation)" + case "iPad7,5", "iPad7,6": + return "iPad (6th generation)" + case "iPad7,11", "iPad7,12": + return "iPad (7th generation)" + case "iPad11,6", "iPad11,7": + return "iPad (8th generation)" + case "iPad4,1", "iPad4,2", "iPad4,3": + return "iPad Air" + case "iPad5,3", "iPad5,4": + return "iPad Air 2" + case "iPad11,3", "iPad11,4": + return "iPad Air (3rd generation)" + case "iPad13,1", "iPad13,2": + return "iPad Air (4th generation)" + case "iPad2,5", "iPad2,6", "iPad2,7": + return "iPad mini" + case "iPad4,4", "iPad4,5", "iPad4,6": + return "iPad mini 2" + case "iPad4,7", "iPad4,8", "iPad4,9": + return "iPad mini 3" + case "iPad5,1", "iPad5,2": + return "iPad mini 4" + case "iPad11,1", "iPad11,2": + return "iPad mini (5th generation)" + case "iPad6,3", "iPad6,4": + return "iPad Pro (9.7-inch)" + case "iPad7,3", "iPad7,4": + return "iPad Pro (10.5-inch)" + case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4": + return "iPad Pro (11-inch) (1st generation)" + case "iPad8,9", "iPad8,10": + return "iPad Pro (11-inch) (2nd generation)" + case "iPad6,7", "iPad6,8": + return "iPad Pro (12.9-inch) (1st generation)" + case "iPad7,1", "iPad7,2": + return "iPad Pro (12.9-inch) (2nd generation)" + case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8": + return "iPad Pro (12.9-inch) (3rd generation)" + case "iPad8,11", "iPad8,12": + return "iPad Pro (12.9-inch) (4th generation)" + case "AppleTV5,3": + return "Apple TV" + case "AppleTV6,2": + return "Apple TV 4K" + case "AudioAccessory1,1": + return "HomePod" + case "AudioAccessory5,1": + return "HomePod mini" + case "i386", "x86_64": + return "Simulator" + default: + return identifier + } +} + +func GetIOSDeviceType(identifier string) string { + if strings.Contains(identifier, "iPhone") { + return "mobile" //"phone" + } + if strings.Contains(identifier, "iPad") { + return "tablet" + } + return "other" +} diff --git a/backend/services/http/ios_device.go b/backend/services/http/ios_device.go deleted file mode 100644 index 2c3474157..000000000 --- a/backend/services/http/ios_device.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "strings" -) - -func MapIOSDevice(identifier string) string { - switch identifier { - case "iPod5,1": return "iPod touch (5th generation)" - case "iPod7,1": return "iPod touch (6th generation)" - case "iPod9,1": return "iPod touch (7th generation)" - case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4" - case "iPhone4,1": return "iPhone 4s" - case "iPhone5,1", "iPhone5,2": return "iPhone 5" - case "iPhone5,3", "iPhone5,4": return "iPhone 5c" - case "iPhone6,1", "iPhone6,2": return "iPhone 5s" - case "iPhone7,2": return "iPhone 6" - case "iPhone7,1": return "iPhone 6 Plus" - case "iPhone8,1": return "iPhone 6s" - case "iPhone8,2": return "iPhone 6s Plus" - case "iPhone8,4": return "iPhone SE" - case "iPhone9,1", "iPhone9,3": return "iPhone 7" - case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus" - case "iPhone10,1", "iPhone10,4": return "iPhone 8" - case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus" - case "iPhone10,3", "iPhone10,6": return "iPhone X" - case "iPhone11,2": return "iPhone XS" - case "iPhone11,4", "iPhone11,6": return "iPhone XS Max" - case "iPhone11,8": return "iPhone XR" - case "iPhone12,1": return "iPhone 11" - case "iPhone12,3": return "iPhone 11 Pro" - case "iPhone12,5": return "iPhone 11 Pro Max" - case "iPhone12,8": return "iPhone SE (2nd generation)" - case "iPhone13,1": return "iPhone 12 mini" - case "iPhone13,2": return "iPhone 12" - case "iPhone13,3": return "iPhone 12 Pro" - case "iPhone13,4": return "iPhone 12 Pro Max" - case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2" - case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad (3rd generation)" - case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad (4th generation)" - case "iPad6,11", "iPad6,12": return "iPad (5th generation)" - case "iPad7,5", "iPad7,6": return "iPad (6th generation)" - case "iPad7,11", "iPad7,12": return "iPad (7th generation)" - case "iPad11,6", "iPad11,7": return "iPad (8th generation)" - case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air" - case "iPad5,3", "iPad5,4": return "iPad Air 2" - case "iPad11,3", "iPad11,4": return "iPad Air (3rd generation)" - case "iPad13,1", "iPad13,2": return "iPad Air (4th generation)" - case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad mini" - case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad mini 2" - case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad mini 3" - case "iPad5,1", "iPad5,2": return "iPad mini 4" - case "iPad11,1", "iPad11,2": return "iPad mini (5th generation)" - case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)" - case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)" - case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4":return "iPad Pro (11-inch) (1st generation)" - case "iPad8,9", "iPad8,10": return "iPad Pro (11-inch) (2nd generation)" - case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch) (1st generation)" - case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)" - case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8":return "iPad Pro (12.9-inch) (3rd generation)" - case "iPad8,11", "iPad8,12": return "iPad Pro (12.9-inch) (4th generation)" - case "AppleTV5,3": return "Apple TV" - case "AppleTV6,2": return "Apple TV 4K" - case "AudioAccessory1,1": return "HomePod" - case "AudioAccessory5,1": return "HomePod mini" - case "i386", "x86_64": return "Simulator" - default: return identifier - } -} - -func GetIOSDeviceType(identifier string) string { - if strings.Contains(identifier, "iPhone") { - return "mobile" //"phone" - } - if strings.Contains(identifier, "iPad") { - return "tablet" - } - return "other" -} \ No newline at end of file diff --git a/backend/services/http/main.go b/backend/services/http/main.go index 8ed8b6d95..f0ae22d32 100644 --- a/backend/services/http/main.go +++ b/backend/services/http/main.go @@ -10,19 +10,17 @@ import ( "golang.org/x/net/http2" - + "openreplay/backend/pkg/db/cache" + "openreplay/backend/pkg/db/postgres" "openreplay/backend/pkg/env" "openreplay/backend/pkg/flakeid" "openreplay/backend/pkg/queue" "openreplay/backend/pkg/queue/types" "openreplay/backend/pkg/storage" - "openreplay/backend/pkg/db/postgres" - "openreplay/backend/pkg/db/cache" - "openreplay/backend/pkg/url/assets" "openreplay/backend/pkg/token" + "openreplay/backend/pkg/url/assets" "openreplay/backend/services/http/geoip" "openreplay/backend/services/http/uaparser" - ) var rewriter *assets.Rewriter @@ -38,6 +36,7 @@ var TOPIC_RAW_WEB string var TOPIC_RAW_IOS string var TOPIC_CACHE string var TOPIC_TRIGGER string + //var TOPIC_ANALYTICS string var CACHE_ASSESTS bool var BEACON_SIZE_LIMIT int64 @@ -53,7 +52,7 @@ func main() { TOPIC_TRIGGER = env.String("TOPIC_TRIGGER") //TOPIC_ANALYTICS = env.String("TOPIC_ANALYTICS") rewriter = assets.NewRewriter(env.String("ASSETS_ORIGIN")) - pgconn = cache.NewPGCache(postgres.NewConn(env.String("POSTGRES_STRING")), 1000 * 60 * 20) + pgconn = cache.NewPGCache(postgres.NewConn(env.String("POSTGRES_STRING")), 1000*60*20) defer pgconn.Close() s3 = storage.NewS3(env.String("AWS_REGION"), env.String("S3_BUCKET_IOS_IMAGES")) tokenizer = token.NewTokenizer(env.String("TOKEN_SECRET")) @@ -70,7 +69,7 @@ func main() { Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // TODO: agree with specification - w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "POST") w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization") if r.Method == http.MethodOptions { @@ -79,13 +78,12 @@ func main() { return } - log.Printf("Request: %v - %v ", r.Method, r.URL.Path) - + log.Printf("Request: %v - %v ", r.Method, r.URL.Path) switch r.URL.Path { case "/": w.WriteHeader(http.StatusOK) - case "/v1/web/not-started": + case "/v1/web/not-started": switch r.Method { case http.MethodPost: notStartedHandlerWeb(w, r) diff --git a/backend/services/http/project_id.go b/backend/services/http/project_id.go deleted file mode 100644 index 059576fe8..000000000 --- a/backend/services/http/project_id.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -func decodeProjectID(projectID uint64) uint64 { - if projectID < 0x10000000000000 || projectID >= 0x20000000000000 { - return 0 - } - projectID = (projectID - 0x10000000000000) * 4212451012670231 & 0xfffffffffffff - if projectID > 0xffffffff { - return 0 - } - return projectID -} From 6196e79d000f69a50b0b5e2e80d7298922b8e2a0 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 1 Apr 2022 19:16:24 +0200 Subject: [PATCH 035/294] feat(backend): everyday update of an assets' version --- backend/pkg/url/assets/url.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/pkg/url/assets/url.go b/backend/pkg/url/assets/url.go index 1fe717531..b55921149 100644 --- a/backend/pkg/url/assets/url.go +++ b/backend/pkg/url/assets/url.go @@ -5,11 +5,18 @@ import ( "path/filepath" "strconv" "strings" + "time" + + "openreplay/backend/pkg/flakeid" ) func getSessionKey(sessionID uint64) string { - // Based on timestamp, changes once per week. Check pkg/flakeid for understanding sessionID - return strconv.FormatUint(sessionID>>50, 10) + return strconv.FormatUint( + uint64(time.UnixMilli( + int64(flakeid.ExtractTimestamp(sessionID)), + ).Weekday()), + 10, + ) } func ResolveURL(baseurl string, rawurl string) string { From c8872064ec3c35f455c4d435a3a9e3826e19d34d Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 1 Apr 2022 19:43:46 +0200 Subject: [PATCH 036/294] code(backend): use 1.18 time features --- backend/pkg/token/tokenizer.go | 12 +- backend/pkg/utime/utime.go | 11 -- backend/services/ender/builder/builder.go | 4 +- backend/services/ender/main.go | 2 +- backend/services/http/handlers-ios.go | 4 +- backend/services/http/handlers-web.go | 8 +- .../integrations/integration/bugsnag.go | 32 +++--- .../integrations/integration/client.go | 37 +++--- .../integrations/integration/datadog.go | 43 ++++--- .../integrations/integration/elasticsearch.go | 3 +- .../integrations/integration/newrelic.go | 32 +++--- .../integrations/integration/sentry.go | 49 ++++---- .../integrations/integration/stackdriver.go | 105 +++++++++--------- .../integrations/integration/sumologic.go | 53 +++++---- ee/backend/pkg/kafka/consumer.go | 2 +- 15 files changed, 184 insertions(+), 213 deletions(-) delete mode 100644 backend/pkg/utime/utime.go diff --git a/backend/pkg/token/tokenizer.go b/backend/pkg/token/tokenizer.go index 3f1069a63..f61e1f145 100644 --- a/backend/pkg/token/tokenizer.go +++ b/backend/pkg/token/tokenizer.go @@ -22,8 +22,8 @@ func NewTokenizer(secret string) *Tokenizer { } type TokenData struct { - ID uint64 - ExpTime int64 + ID uint64 + ExpTime int64 } func (tokenizer *Tokenizer) sign(body string) []byte { @@ -33,7 +33,7 @@ func (tokenizer *Tokenizer) sign(body string) []byte { } func (tokenizer *Tokenizer) Compose(d TokenData) string { - body := strconv.FormatUint(d.ID, 36) + + body := strconv.FormatUint(d.ID, 36) + "." + strconv.FormatInt(d.ExpTime, 36) sign := base58.Encode(tokenizer.sign(body)) return body + "." + sign @@ -58,8 +58,8 @@ func (tokenizer *Tokenizer) Parse(token string) (*TokenData, error) { if err != nil { return nil, err } - if expTime <= time.Now().UnixNano()/1e6 { - return &TokenData{id,expTime}, EXPIRED + if expTime <= time.Now().UnixMilli() { + return &TokenData{id, expTime}, EXPIRED } - return &TokenData{id,expTime}, nil + return &TokenData{id, expTime}, nil } diff --git a/backend/pkg/utime/utime.go b/backend/pkg/utime/utime.go deleted file mode 100644 index e3b5a2751..000000000 --- a/backend/pkg/utime/utime.go +++ /dev/null @@ -1,11 +0,0 @@ -package utime - -import "time" - -func CurrentTimestamp() int64 { - return time.Now().UnixNano() / 1e6 -} - -func ToMilliseconds(t time.Time) int64 { - return t.UnixNano() / 1e6 -} diff --git a/backend/services/ender/builder/builder.go b/backend/services/ender/builder/builder.go index e36bdcbe3..9c2067985 100644 --- a/backend/services/ender/builder/builder.go +++ b/backend/services/ender/builder/builder.go @@ -110,11 +110,11 @@ func (b *builder) buildInputEvent() { func (b *builder) handleMessage(message Message, messageID uint64) { timestamp := GetTimestamp(message) - if b.timestamp <= timestamp { // unnecessary? TODO: test and remove + if b.timestamp < timestamp { // unnecessary? TODO: test and remove b.timestamp = timestamp } - b.lastProcessedTimestamp = time.Now().UnixNano() / 1e6 + b.lastProcessedTimestamp = time.Now().UnixMilli() // Might happen before the first timestamp. switch msg := message.(type) { diff --git a/backend/services/ender/main.go b/backend/services/ender/main.go index f0f139dce..f2430f3a0 100644 --- a/backend/services/ender/main.go +++ b/backend/services/ender/main.go @@ -56,7 +56,7 @@ func main() { consumer.Close() os.Exit(0) case <-tick: - builderMap.IterateReadyMessages(time.Now().UnixNano()/1e6, func(sessionID uint64, readyMsg messages.Message) { + builderMap.IterateReadyMessages(time.Now().UnixMilli(), func(sessionID uint64, readyMsg messages.Message) { producer.Produce(TOPIC_TRIGGER, sessionID, messages.Encode(readyMsg)) }) // TODO: why exactly do we need Flush here and not in any other place? diff --git a/backend/services/http/handlers-ios.go b/backend/services/http/handlers-ios.go index affcab59d..f15a6af60 100644 --- a/backend/services/http/handlers-ios.go +++ b/backend/services/http/handlers-ios.go @@ -85,14 +85,14 @@ func startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) { responseWithError(w, http.StatusForbidden, errors.New("browser not recognized")) return } - sessionID, err := flaker.Compose(uint64(startTime.UnixNano() / 1e6)) + sessionID, err := flaker.Compose(uint64(startTime.UnixMilli())) if err != nil { responseWithError(w, http.StatusInternalServerError, err) return } // TODO: if EXPIRED => send message for two sessions association expTime := startTime.Add(time.Duration(p.MaxSessionDuration) * time.Millisecond) - tokenData = &token.TokenData{sessionID, expTime.UnixNano() / 1e6} + tokenData = &token.TokenData{sessionID, expTime.UnixMilli()} country := geoIP.ExtractISOCodeFromHTTPRequest(r) diff --git a/backend/services/http/handlers-web.go b/backend/services/http/handlers-web.go index 6020c3eb1..29dcf161d 100644 --- a/backend/services/http/handlers-web.go +++ b/backend/services/http/handlers-web.go @@ -76,14 +76,14 @@ func startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) { responseWithError(w, http.StatusForbidden, errors.New("browser not recognized")) return } - sessionID, err := flaker.Compose(uint64(startTime.UnixNano() / 1e6)) + sessionID, err := flaker.Compose(uint64(startTime.UnixMilli())) if err != nil { responseWithError(w, http.StatusInternalServerError, err) return } // TODO: if EXPIRED => send message for two sessions association expTime := startTime.Add(time.Duration(p.MaxSessionDuration) * time.Millisecond) - tokenData = &token.TokenData{sessionID, expTime.UnixNano() / 1e6} + tokenData = &token.TokenData{sessionID, expTime.UnixMilli()} country := geoIP.ExtractISOCodeFromHTTPRequest(r) producer.Produce(TOPIC_RAW_WEB, tokenData.ID, Encode(&SessionStart{ @@ -108,8 +108,8 @@ func startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) { //delayDuration := time.Now().Sub(startTime) responseWithJSON(w, &response{ - //Timestamp: startTime.UnixNano() / 1e6, - //Delay: delayDuration.Nanoseconds() / 1e6, + //Timestamp: startTime.UnixMilli(), + //Delay: delayDuration.Milliseconds(), Token: tokenizer.Compose(*tokenData), UserUUID: userUUID, SessionID: strconv.FormatUint(tokenData.ID, 10), diff --git a/backend/services/integrations/integration/bugsnag.go b/backend/services/integrations/integration/bugsnag.go index 7c31db3cb..118cdb84d 100644 --- a/backend/services/integrations/integration/bugsnag.go +++ b/backend/services/integrations/integration/bugsnag.go @@ -1,15 +1,14 @@ package integration import ( + "encoding/json" "fmt" + "io" + "io/ioutil" "net/http" - "encoding/json" "net/url" "time" - "io" - "io/ioutil" - "openreplay/backend/pkg/utime" "openreplay/backend/pkg/messages" ) @@ -18,15 +17,14 @@ import ( */ type bugsnag struct { - BugsnagProjectId string // `json:"bugsnag_project_id"` + BugsnagProjectId string // `json:"bugsnag_project_id"` AuthorizationToken string // `json:"auth_token"` } - type bugsnagEvent struct { MetaData struct { SpecialInfo struct { - AsayerSessionId uint64 `json:"asayerSessionId,string"` + AsayerSessionId uint64 `json:"asayerSessionId,string"` OpenReplaySessionToken string `json:"openReplaySessionToken"` } `json:"special_info"` } `json:"metaData"` @@ -38,7 +36,7 @@ type bugsnagEvent struct { func (b *bugsnag) Request(c *client) error { sinceTs := c.getLastMessageTimestamp() + 1000 // From next second - sinceFormatted := time.Unix(0, int64(sinceTs*1e6)).Format(time.RFC3339) + sinceFormatted := time.UnixMilli(int64(sinceTs)).Format(time.RFC3339) requestURL := fmt.Sprintf("https://api.bugsnag.com/projects/%v/events", b.BugsnagProjectId) req, err := http.NewRequest("GET", requestURL, nil) if err != nil { @@ -47,10 +45,10 @@ func (b *bugsnag) Request(c *client) error { q := req.URL.Query() // q.Add("per_page", "100") // Up to a maximum of 30. Default: 30 // q.Add("sort", "timestamp") // Default: timestamp (timestamp == ReceivedAt ??) - q.Add("direction", "asc") // Default: desc + q.Add("direction", "asc") // Default: desc q.Add("full_reports", "true") // Default: false - q.Add("filters[event.since][][type]", "eq") - q.Add("filters[event.since][][value]", sinceFormatted) // seems like inclusively + q.Add("filters[event.since][][type]", "eq") + q.Add("filters[event.since][][value]", sinceFormatted) // seems like inclusively req.URL.RawQuery = q.Encode() authToken := "token " + b.AuthorizationToken @@ -85,7 +83,7 @@ func (b *bugsnag) Request(c *client) error { } sessionID := e.MetaData.SpecialInfo.AsayerSessionId token := e.MetaData.SpecialInfo.OpenReplaySessionToken - if sessionID == 0 && token == "" { + if sessionID == 0 && token == "" { // c.errChan <- "No AsayerSessionId found. | Message: %v", e continue } @@ -94,16 +92,16 @@ func (b *bugsnag) Request(c *client) error { c.errChan <- err continue } - timestamp := uint64(utime.ToMilliseconds(parsedTime)) + timestamp := uint64(parsedTime.UnixMilli()) c.setLastMessageTimestamp(timestamp) c.evChan <- &SessionErrorEvent{ SessionID: sessionID, - Token: token, + Token: token, RawErrorEvent: &messages.RawErrorEvent{ - Source: "bugsnag", + Source: "bugsnag", Timestamp: timestamp, - Name: e.Exceptions[0].Message, - Payload: string(jsonEvent), + Name: e.Exceptions[0].Message, + Payload: string(jsonEvent), }, } } diff --git a/backend/services/integrations/integration/client.go b/backend/services/integrations/integration/client.go index 2abf9913d..315bfe4e9 100644 --- a/backend/services/integrations/integration/client.go +++ b/backend/services/integrations/integration/client.go @@ -5,10 +5,10 @@ import ( "fmt" "log" "sync" + "time" "openreplay/backend/pkg/db/postgres" "openreplay/backend/pkg/messages" - "openreplay/backend/pkg/utime" ) const MAX_ATTEMPTS_IN_A_ROW = 4 @@ -20,10 +20,10 @@ type requester interface { } type requestData struct { - LastMessageTimestamp uint64 // `json:"lastMessageTimestamp, string"` - LastMessageId string + LastMessageTimestamp uint64 // `json:"lastMessageTimestamp, string"` + LastMessageId string UnsuccessfullAttemptsCount int - LastAttemptTimestamp int64 + LastAttemptTimestamp int64 } type client struct { @@ -31,19 +31,19 @@ type client struct { requester integration *postgres.Integration // TODO: timeout ? - mux sync.Mutex + mux sync.Mutex updateChan chan<- postgres.Integration - evChan chan<- *SessionErrorEvent - errChan chan<- error + evChan chan<- *SessionErrorEvent + errChan chan<- error } type SessionErrorEvent struct { SessionID uint64 - Token string + Token string *messages.RawErrorEvent } -type ClientMap map[ string ]*client +type ClientMap map[string]*client func NewClient(i *postgres.Integration, updateChan chan<- postgres.Integration, evChan chan<- *SessionErrorEvent, errChan chan<- error) (*client, error) { c := new(client) @@ -60,15 +60,14 @@ func NewClient(i *postgres.Integration, updateChan chan<- postgres.Integration, // TODO: RequestData manager if c.requestData.LastMessageTimestamp == 0 { // ? - c.requestData.LastMessageTimestamp = uint64(utime.CurrentTimestamp() - 24*60*60*1000) + c.requestData.LastMessageTimestamp = uint64(time.Now().Add(-time.Hour * 24).UnixMilli()) } return c, nil } - // from outside -func (c* client) Update(i *postgres.Integration) error { +func (c *client) Update(i *postgres.Integration) error { c.mux.Lock() defer c.mux.Unlock() var r requester @@ -111,8 +110,8 @@ func (c *client) getLastMessageTimestamp() uint64 { } func (c *client) setLastMessageId(timestamp uint64, id string) { //if timestamp >= c.requestData.LastMessageTimestamp { - c.requestData.LastMessageId = id - c.requestData.LastMessageTimestamp = timestamp + c.requestData.LastMessageId = id + c.requestData.LastMessageTimestamp = timestamp //} } func (c *client) getLastMessageId() string { @@ -128,18 +127,18 @@ func (c *client) Request() { c.mux.Lock() defer c.mux.Unlock() if c.requestData.UnsuccessfullAttemptsCount >= MAX_ATTEMPTS || - (c.requestData.UnsuccessfullAttemptsCount >= MAX_ATTEMPTS_IN_A_ROW && - utime.CurrentTimestamp() - c.requestData.LastAttemptTimestamp < ATTEMPTS_INTERVAL) { + (c.requestData.UnsuccessfullAttemptsCount >= MAX_ATTEMPTS_IN_A_ROW && + time.Now().UnixMilli()-c.requestData.LastAttemptTimestamp < ATTEMPTS_INTERVAL) { return } - c.requestData.LastAttemptTimestamp = utime.CurrentTimestamp() + c.requestData.LastAttemptTimestamp = time.Now().UnixMilli() err := c.requester.Request(c) if err != nil { log.Println("ERRROR L139") log.Println(err) c.handleError(err) - c.requestData.UnsuccessfullAttemptsCount++; + c.requestData.UnsuccessfullAttemptsCount++ } else { c.requestData.UnsuccessfullAttemptsCount = 0 } @@ -152,5 +151,3 @@ func (c *client) Request() { c.integration.RequestData = rd c.updateChan <- *c.integration } - - diff --git a/backend/services/integrations/integration/datadog.go b/backend/services/integrations/integration/datadog.go index eb7b5daee..096c3b822 100644 --- a/backend/services/integrations/integration/datadog.go +++ b/backend/services/integrations/integration/datadog.go @@ -1,38 +1,37 @@ package integration import ( - "fmt" - "net/http" - "encoding/json" "bytes" - "time" + "encoding/json" + "fmt" "io" - "io/ioutil" + "io/ioutil" + "net/http" + "time" - "openreplay/backend/pkg/utime" "openreplay/backend/pkg/messages" ) -/* +/* We collect Logs. Datadog also has Events */ type datadog struct { - ApplicationKey string //`json:"application_key"` - ApiKey string //`json:"api_key"` + ApplicationKey string //`json:"application_key"` + ApiKey string //`json:"api_key"` } type datadogResponce struct { - Logs []json.RawMessage + Logs []json.RawMessage NextLogId *string - Status string + Status string } type datadogLog struct { Content struct { - Timestamp string - Message string + Timestamp string + Message string Attributes struct { Error struct { // Not sure about this Message string @@ -48,10 +47,10 @@ func (d *datadog) makeRequest(nextLogId *string, fromTs uint64, toTs uint64) (*h d.ApplicationKey, ) startAt := "null" - if nextLogId != nil && *nextLogId != "" { + if nextLogId != nil && *nextLogId != "" { startAt = *nextLogId } - // Query: status:error/info/warning? + // Query: status:error/info/warning? // openReplaySessionToken instead of asayer_session_id jsonBody := fmt.Sprintf(`{ "limit": 1000, @@ -72,8 +71,8 @@ func (d *datadog) makeRequest(nextLogId *string, fromTs uint64, toTs uint64) (*h } func (d *datadog) Request(c *client) error { - fromTs := c.getLastMessageTimestamp() + 1 // From next millisecond - toTs := uint64(utime.CurrentTimestamp()) + fromTs := c.getLastMessageTimestamp() + 1 // From next millisecond + toTs := uint64(time.Now().UnixMilli()) var nextLogId *string for { req, err := d.makeRequest(nextLogId, fromTs, toTs) @@ -111,16 +110,16 @@ func (d *datadog) Request(c *client) error { c.errChan <- err continue } - timestamp := uint64(utime.ToMilliseconds(parsedTime)) + timestamp := uint64(parsedTime.UnixMilli()) c.setLastMessageTimestamp(timestamp) c.evChan <- &SessionErrorEvent{ //SessionID: sessionID, Token: token, RawErrorEvent: &messages.RawErrorEvent{ - Source: "datadog", + Source: "datadog", Timestamp: timestamp, - Name: ddLog.Content.Attributes.Error.Message, - Payload: string(jsonLog), + Name: ddLog.Content.Attributes.Error.Message, + Payload: string(jsonLog), }, } } @@ -129,4 +128,4 @@ func (d *datadog) Request(c *client) error { return nil } } -} \ No newline at end of file +} diff --git a/backend/services/integrations/integration/elasticsearch.go b/backend/services/integrations/integration/elasticsearch.go index 14480e0b8..dd6f5d5f9 100644 --- a/backend/services/integrations/integration/elasticsearch.go +++ b/backend/services/integrations/integration/elasticsearch.go @@ -12,7 +12,6 @@ import ( "time" "openreplay/backend/pkg/messages" - "openreplay/backend/pkg/utime" ) type elasticsearch struct { @@ -164,7 +163,7 @@ func (es *elasticsearch) Request(c *client) error { c.errChan <- err continue } - timestamp := uint64(utime.ToMilliseconds(esLog.Time)) + timestamp := uint64(esLog.Time.UnixMilli()) c.setLastMessageTimestamp(timestamp) var sessionID uint64 diff --git a/backend/services/integrations/integration/newrelic.go b/backend/services/integrations/integration/newrelic.go index 937ab166d..2dce79aa5 100644 --- a/backend/services/integrations/integration/newrelic.go +++ b/backend/services/integrations/integration/newrelic.go @@ -2,25 +2,24 @@ package integration import ( "encoding/json" - "time" + "errors" "fmt" - "net/http" "io" - "io/ioutil" - "errors" + "io/ioutil" + "net/http" + "time" "openreplay/backend/pkg/messages" ) /* - We use insights-api for query. They also have Logs and Events + We use insights-api for query. They also have Logs and Events */ - // TODO: Eu/us type newrelic struct { - ApplicationId string //`json:"application_id"` - XQueryKey string //`json:"x_query_key"` + ApplicationId string //`json:"application_id"` + XQueryKey string //`json:"x_query_key"` } // TODO: Recheck @@ -34,14 +33,14 @@ type newrelicResponce struct { type newrelicEvent struct { //AsayerSessionID uint64 `json:"asayer_session_id,string"` // string/int decoder? OpenReplaySessionToken string `json:"openReplaySessionToken"` - ErrorClass string `json:"error.class"` - Timestamp uint64 `json:"timestamp"` + ErrorClass string `json:"error.class"` + Timestamp uint64 `json:"timestamp"` } func (nr *newrelic) Request(c *client) error { sinceTs := c.getLastMessageTimestamp() + 1000 // From next second // In docs - format "yyyy-mm-dd HH:MM:ss", but time.RFC3339 works fine too - sinceFormatted := time.Unix(0, int64(sinceTs*1e6)).Format(time.RFC3339) + sinceFormatted := time.UnixMilli(int64(sinceTs)).Format(time.RFC3339) // US/EU endpoint ?? requestURL := fmt.Sprintf("https://insights-api.eu.newrelic.com/v1/accounts/%v/query", nr.ApplicationId) req, err := http.NewRequest("GET", requestURL, nil) @@ -64,11 +63,10 @@ func (nr *newrelic) Request(c *client) error { } defer resp.Body.Close() - // 401 (unauthorised) if wrong XQueryKey/deploymentServer is wrong or 403 (Forbidden) if ApplicationId is wrong // 400 if Query has problems if resp.StatusCode >= 400 { - io.Copy(ioutil.Discard, resp.Body) // Read the body to free socket + io.Copy(ioutil.Discard, resp.Body) // Read the body to free socket return fmt.Errorf("Newrelic: server respond with the code %v| Request: ", resp.StatusCode, *req) } // Pagination depending on returning metadata ? @@ -92,13 +90,13 @@ func (nr *newrelic) Request(c *client) error { c.evChan <- &SessionErrorEvent{ Token: e.OpenReplaySessionToken, RawErrorEvent: &messages.RawErrorEvent{ - Source: "newrelic", + Source: "newrelic", Timestamp: e.Timestamp, - Name: e.ErrorClass, - Payload: string(jsonEvent), + Name: e.ErrorClass, + Payload: string(jsonEvent), }, } } } return nil -} \ No newline at end of file +} diff --git a/backend/services/integrations/integration/sentry.go b/backend/services/integrations/integration/sentry.go index 0330430c3..1c5bfdaad 100644 --- a/backend/services/integrations/integration/sentry.go +++ b/backend/services/integrations/integration/sentry.go @@ -1,44 +1,41 @@ package integration import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" "net/http" "net/url" - "encoding/json" - "strings" - "fmt" - "time" "strconv" - "io" - "io/ioutil" + "strings" + "time" - "openreplay/backend/pkg/utime" "openreplay/backend/pkg/messages" ) - -/* +/* They also have different stuff - Documentation says: + Documentation says: "Note: This endpoint is experimental and may be removed without notice." */ type sentry struct { OrganizationSlug string // `json:"organization_slug"` - ProjectSlug string // `json:"project_slug"` - Token string // `json:"token"` + ProjectSlug string // `json:"project_slug"` + Token string // `json:"token"` } type sentryEvent struct { Tags []struct { - Key string - Value string `json:"value"` + Key string + Value string `json:"value"` } - DateCreated string `json:"dateCreated"` // or dateReceived ? - Title string - EventID string `json:"eventID"` + DateCreated string `json:"dateCreated"` // or dateReceived ? + Title string + EventID string `json:"eventID"` } - func (sn *sentry) Request(c *client) error { requestURL := fmt.Sprintf("https://sentry.io/api/0/projects/%v/%v/events/", sn.OrganizationSlug, sn.ProjectSlug) req, err := http.NewRequest("GET", requestURL, nil) @@ -88,9 +85,9 @@ PageLoop: c.errChan <- fmt.Errorf("%v | Event: %v", err, e) continue } - timestamp := uint64(utime.ToMilliseconds(parsedTime)) + timestamp := uint64(parsedTime.UnixMilli()) // TODO: not to receive all the messages (use default integration timestamp) - if firstEvent { // TODO: reverse range? + if firstEvent { // TODO: reverse range? c.setLastMessageId(timestamp, e.EventID) firstEvent = false } @@ -117,12 +114,12 @@ PageLoop: c.evChan <- &SessionErrorEvent{ SessionID: sessionID, - Token: token, + Token: token, RawErrorEvent: &messages.RawErrorEvent{ - Source: "sentry", + Source: "sentry", Timestamp: timestamp, - Name: e.Title, - Payload: string(jsonEvent), + Name: e.Title, + Payload: string(jsonEvent), }, } } @@ -137,7 +134,7 @@ PageLoop: return fmt.Errorf("Link header format error. Got: '%v'", linkHeader) } - nextLinkInfo := pagInfo[ 1 ] + nextLinkInfo := pagInfo[1] if strings.Contains(nextLinkInfo, `results="false"`) { break } @@ -151,4 +148,4 @@ PageLoop: } } return nil -} \ No newline at end of file +} diff --git a/backend/services/integrations/integration/stackdriver.go b/backend/services/integrations/integration/stackdriver.go index bb8e3cef9..e852d5d36 100644 --- a/backend/services/integrations/integration/stackdriver.go +++ b/backend/services/integrations/integration/stackdriver.go @@ -1,22 +1,19 @@ package integration - import ( - "google.golang.org/api/option" "cloud.google.com/go/logging/logadmin" "google.golang.org/api/iterator" - - //"strconv" - "encoding/json" - "time" - "fmt" - "context" + "google.golang.org/api/option" + + //"strconv" + "context" + "encoding/json" + "fmt" + "time" - "openreplay/backend/pkg/utime" "openreplay/backend/pkg/messages" ) - // Old: asayerSessionId const SD_FILTER_QUERY = ` @@ -28,7 +25,7 @@ const SD_FILTER_QUERY = ` type stackdriver struct { ServiceAccountCredentials string // `json:"service_account_credentials"` - LogName string // `json:"log_name"` + LogName string // `json:"log_name"` } type saCreds struct { @@ -37,10 +34,10 @@ type saCreds struct { func (sd *stackdriver) Request(c *client) error { fromTs := c.getLastMessageTimestamp() + 1 // Timestamp is RFC3339Nano, so we take the next millisecond - fromFormatted := time.Unix(0, int64(fromTs *1e6)).Format(time.RFC3339Nano) + fromFormatted := time.UnixMilli(int64(fromTs)).Format(time.RFC3339Nano) ctx := context.Background() - var parsedCreds saCreds + var parsedCreds saCreds err := json.Unmarshal([]byte(sd.ServiceAccountCredentials), &parsedCreds) if err != nil { return err @@ -49,56 +46,56 @@ func (sd *stackdriver) Request(c *client) error { opt := option.WithCredentialsJSON([]byte(sd.ServiceAccountCredentials)) client, err := logadmin.NewClient(ctx, parsedCreds.ProjectId, opt) if err != nil { - return err + return err } defer client.Close() - - filter := fmt.Sprintf(SD_FILTER_QUERY, parsedCreds.ProjectId, sd.LogName, fromFormatted) - // By default, Entries are listed from oldest to newest. - /* ResourceNames(rns []string) - "projects/[PROJECT_ID]" - "organizations/[ORGANIZATION_ID]" - "billingAccounts/[BILLING_ACCOUNT_ID]" - "folders/[FOLDER_ID]" - */ - it := client.Entries(ctx, logadmin.Filter(filter)) - // TODO: Pagination: - //pager := iterator.NewPager(it, 1000, "") - //nextToken, err := pager.NextPage(&entries) - //if nextToken == "" { break } - for { - e, err := it.Next() - if err == iterator.Done { - break - } - if err != nil { - return err - } + filter := fmt.Sprintf(SD_FILTER_QUERY, parsedCreds.ProjectId, sd.LogName, fromFormatted) + // By default, Entries are listed from oldest to newest. + /* ResourceNames(rns []string) + "projects/[PROJECT_ID]" + "organizations/[ORGANIZATION_ID]" + "billingAccounts/[BILLING_ACCOUNT_ID]" + "folders/[FOLDER_ID]" + */ + it := client.Entries(ctx, logadmin.Filter(filter)) - token := e.Labels["openReplaySessionToken"] - // sessionID, err := strconv.ParseUint(strSessionID, 10, 64) - // if err != nil { - // c.errChan <- err - // continue - // } - jsonEvent, err := json.Marshal(e) - if err != nil { - c.errChan <- err - continue - } - timestamp := uint64(utime.ToMilliseconds(e.Timestamp)) - c.setLastMessageTimestamp(timestamp) - c.evChan <- &SessionErrorEvent{ + // TODO: Pagination: + //pager := iterator.NewPager(it, 1000, "") + //nextToken, err := pager.NextPage(&entries) + //if nextToken == "" { break } + for { + e, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return err + } + + token := e.Labels["openReplaySessionToken"] + // sessionID, err := strconv.ParseUint(strSessionID, 10, 64) + // if err != nil { + // c.errChan <- err + // continue + // } + jsonEvent, err := json.Marshal(e) + if err != nil { + c.errChan <- err + continue + } + timestamp := uint64(e.Timestamp.UnixMilli()) + c.setLastMessageTimestamp(timestamp) + c.evChan <- &SessionErrorEvent{ //SessionID: sessionID, Token: token, RawErrorEvent: &messages.RawErrorEvent{ - Source: "stackdriver", + Source: "stackdriver", Timestamp: timestamp, - Name: e.InsertID, // not sure about that - Payload: string(jsonEvent), + Name: e.InsertID, // not sure about that + Payload: string(jsonEvent), }, } } return nil -} \ No newline at end of file +} diff --git a/backend/services/integrations/integration/sumologic.go b/backend/services/integrations/integration/sumologic.go index 2660dd6ac..8ff39ec9e 100644 --- a/backend/services/integrations/integration/sumologic.go +++ b/backend/services/integrations/integration/sumologic.go @@ -1,20 +1,19 @@ package integration import ( - "net/http" - "time" "encoding/json" "fmt" - "strings" "io" - "io/ioutil" + "io/ioutil" + "net/http" + "strings" + "time" - "openreplay/backend/pkg/utime" "openreplay/backend/pkg/messages" ) -/* - The maximum value for limit is 10,000 messages or 100 MB in total message size, +/* + The maximum value for limit is 10,000 messages or 100 MB in total message size, which means the query may return less than 10,000 messages if you exceed the size limit. API Documentation: https://help.sumologic.com/APIs/Search-Job-API/About-the-Search-Job-API @@ -22,31 +21,30 @@ import ( const SL_LIMIT = 10000 type sumologic struct { - AccessId string // `json:"access_id"` - AccessKey string // `json:"access_key"` - cookies []*http.Cookie + AccessId string // `json:"access_id"` + AccessKey string // `json:"access_key"` + cookies []*http.Cookie } - type sumplogicJobResponce struct { Id string } type sumologicJobStatusResponce struct { - State string + State string MessageCount int //PendingErrors []string } type sumologicResponce struct { - Messages [] struct { + Messages []struct { Map json.RawMessage } } type sumologicEvent struct { Timestamp uint64 `json:"_messagetime,string"` - Raw string `json:"_raw"` + Raw string `json:"_raw"` } func (sl *sumologic) deleteJob(jobId string, errChan chan<- error) { @@ -68,10 +66,9 @@ func (sl *sumologic) deleteJob(jobId string, errChan chan<- error) { resp.Body.Close() } - func (sl *sumologic) Request(c *client) error { fromTs := c.getLastMessageTimestamp() + 1 // From next millisecond - toTs := utime.CurrentTimestamp() + toTs := time.Now().UnixMilli() requestURL := fmt.Sprintf("https://api.%vsumologic.com/api/v1/search/jobs", "eu.") // deployment server?? jsonBody := fmt.Sprintf(`{ "query": "\"openReplaySessionToken=\" AND (*error* OR *fail* OR *exception*)", @@ -132,7 +129,7 @@ func (sl *sumologic) Request(c *client) error { tick := time.Tick(5 * time.Second) for { - <- tick + <-tick resp, err = http.DefaultClient.Do(req) if err != nil { return err // TODO: retry, counter/timeout @@ -147,12 +144,12 @@ func (sl *sumologic) Request(c *client) error { } if jobStatus.State == "DONE GATHERING RESULTS" { offset := 0 - for ;offset < jobStatus.MessageCount; { + for offset < jobStatus.MessageCount { requestURL = fmt.Sprintf( - "https://api.%vsumologic.com/api/v1/search/jobs/%v/messages?offset=%v&limit=%v", - "eu.", - jobResponce.Id, - offset, + "https://api.%vsumologic.com/api/v1/search/jobs/%v/messages?offset=%v&limit=%v", + "eu.", + jobResponce.Id, + offset, SL_LIMIT, ) req, err = http.NewRequest("GET", requestURL, nil) @@ -190,17 +187,17 @@ func (sl *sumologic) Request(c *client) error { } name := e.Raw if len(name) > 20 { - name = name[:20] // not sure about that + name = name[:20] // not sure about that } c.setLastMessageTimestamp(e.Timestamp) c.evChan <- &SessionErrorEvent{ //SessionID: sessionID, Token: token, RawErrorEvent: &messages.RawErrorEvent{ - Source: "sumologic", + Source: "sumologic", Timestamp: e.Timestamp, - Name: name, - Payload: string(m.Map), //e.Raw ? + Name: name, + Payload: string(m.Map), //e.Raw ? }, } @@ -209,11 +206,11 @@ func (sl *sumologic) Request(c *client) error { } break } - if jobStatus.State != "NOT STARTED" && + if jobStatus.State != "NOT STARTED" && jobStatus.State != "GATHERING RESULTS" { // error break } } return nil -} \ No newline at end of file +} diff --git a/ee/backend/pkg/kafka/consumer.go b/ee/backend/pkg/kafka/consumer.go index 82aa56d50..cb3714316 100644 --- a/ee/backend/pkg/kafka/consumer.go +++ b/ee/backend/pkg/kafka/consumer.go @@ -147,7 +147,7 @@ func (consumer *Consumer) ConsumeNext() error { if e.TopicPartition.Error != nil { return errors.Wrap(e.TopicPartition.Error, "Consumer Partition Error") } - ts := e.Timestamp.UnixNano() / 1e6 + ts := e.Timestamp.UnixMilli() consumer.messageHandler(decodeKey(e.Key), e.Value, &types.Meta{ Topic: *(e.TopicPartition.Topic), ID: uint64(e.TopicPartition.Offset), From 1807174b8be9b1755588e954f72119798ada9125 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 1 Apr 2022 20:29:13 +0200 Subject: [PATCH 037/294] 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 d7710356e94505e2146528412f9517709168b2f2 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 1 Apr 2022 22:15:51 +0200 Subject: [PATCH 038/294] feat(tracker-fetch):3.5.3: common improved sanitiser --- tracker/tracker-fetch/README.md | 123 ++++++++++++++++++++++++----- tracker/tracker-fetch/package.json | 2 +- tracker/tracker-fetch/src/index.ts | 122 +++++++++++++++------------- 3 files changed, 170 insertions(+), 77 deletions(-) diff --git a/tracker/tracker-fetch/README.md b/tracker/tracker-fetch/README.md index b7fca2e4b..53fddcf8e 100644 --- a/tracker/tracker-fetch/README.md +++ b/tracker/tracker-fetch/README.md @@ -1,7 +1,6 @@ -# OpenReplay Tracker Fetch plugin +# Fetch plugin for OpenReplay -Tracker plugin to support tracking of the `fetch` requests payload. -Additionally it populates the requests with `sessionToken` header for backend logging. +This plugin allows you to capture `fetch` payloads and inspect them later on while replaying session recordings. This is very useful for understanding and fixing issues. ## Installation @@ -11,36 +10,120 @@ npm i @openreplay/tracker-fetch ## Usage -Initialize the `@openreplay/tracker` package as usual and load the plugin into it. -Then you can use the provided `fetch` method from the plugin instead of built-in. +Use the provided `fetch` method from the plugin instead of the one built-in. + +### If your website is a Single Page Application (SPA) ```js -import Tracker from '@openreplay/tracker'; +import tracker from '@openreplay/tracker'; import trackerFetch from '@openreplay/tracker-fetch'; -const tracker = new Tracker({ - projectKey: YOUR_PROJECT_KEY, +const tracker = new OpenReplay({ + projectKey: PROJECT_KEY }); +const fetch = tracker.use(trackerFetch(options)); // check list of available options below + tracker.start(); -export const fetch = tracker.use(trackerFetch({ /* options here*/ })); - -fetch('https://my.api.io/resource').then(response => response.json()).then(body => console.log(body)); +fetch('https://myapi.com/').then(response => console.log(response.json())); ``` -Options: -```ts -{ - failuresOnly: boolean, // default false - sessionTokenHeader: string | undefined, // default undefined - ignoreHeaders: Array | boolean, // default [ 'Cookie', 'Set-Cookie', 'Authorization' ] +### If your web app is Server-Side-Rendered (SSR) + +Follow the below example if your app is SSR. Ensure `tracker.start()` is called once the app is started (in `useEffect` or `componentDidMount`). + +```js +import OpenReplay from '@openreplay/tracker/cjs'; +import trackerFetch from '@openreplay/tracker-fetch/cjs'; + +const tracker = new OpenReplay({ + projectKey: PROJECT_KEY +}); +const fetch = tracker.use(trackerFetch(options)); // check list of available options below + +//... +function MyApp() { + useEffect(() => { // use componentDidMount in case of React Class Component + tracker.start(); + + fetch('https://myapi.com/').then(response => console.log(response.json())); + }, []) +//... } ``` -Set `failuresOnly` option to `true` if you want to record only requests with the status code >= 400. +## Options -In case you use [OpenReplay integrations (sentry, bugsnag or others)](https://docs.openreplay.com/integrations), you can use `sessionTokenHeader` option to specify the header name. This header will be appended automatically to the each fetch request and will contain OpenReplay session identificator value. +```js +trackerFetch({ + overrideGlobal: boolean; + failuresOnly: boolean; + sessionTokenHeader: string; + ignoreHeaders: Array | boolean; + sanitiser: (RequestResponseData) => RequestResponseData | null; +}) +``` -You can define list of headers that you don't want to capture with the `ignoreHeaders` options. Set its value to `false` if you want to catch them all (`true` if opposite). By default plugin ignores the list of headers that might be sensetive such as `[ 'Cookie', 'Set-Cookie', 'Authorization' ]`. +- `overrideGlobal`: Overrides the default `window.fetch`. Default: `false`. +- `failuresOnly`: Captures requests having 4xx-5xx HTTP status code. Default: `false`. +- `sessionTokenHeader`: In case you have enabled some of our backend [integrations](/integrations) (i.e. Sentry), you can use this option to specify the header name (i.e. 'X-OpenReplay-SessionToken'). This latter gets appended automatically to each fetch request to contain the OpenReplay sessionToken's value. Default: `undefined`. +- `ignoreHeaders`: Helps define a list of headers you don't wish to capture. Set its value to `false` to capture all of them (`true` if none). Default: `['Cookie', 'Set-Cookie', 'Authorization']` so sensitive headers won't be captured. +- `sanitiser`: Sanitise sensitive data from fetch request/response or ignore request comletely. You can redact fields on the request object by modifying then returning it from the function: +```typescript +interface RequestData { + body: BodyInit | null | undefined; // whatewer you've put in the init.body in fetch(url, init) + headers: Record; +} + +interface ResponseData { + body: string | Object | null; // Object if response is of JSON type + headers: Record; +} + +interface RequestResponseData { + readonly status: number; + readonly method: string; + url: string; + request: RequestData; + response: ResponseData; +} + +sanitiser: (data: RequestResponseData) => { // sanitise the body or headers + if (data.url === "/auth") { + data.request.body = null + } + + if (data.request.headers['x-auth-token']) { // can also use ignoreHeaders option instead + data.request.headers['x-auth-token'] = 'SANITISED'; + } + + // Sanitise response + if (data.status < 400 && data.response.body.token) { + data.response.body.token = "" + } + + return data +} + +// OR + +sanitiser: data => { // ignore requests that start with /secure + if (data.url.startsWith("/secure")) { + return null + } + return data +} + +// OR + +sanitiser: data => { // sanitise request url: replace all numbers + data.url = data.url.replace(/\d/g, "*") + return data +} +``` + +## Troubleshooting + +Having trouble setting up this plugin? please connect to our [Discord](https://discord.openreplay.com) and get help from our community. \ No newline at end of file diff --git a/tracker/tracker-fetch/package.json b/tracker/tracker-fetch/package.json index 0b1373edc..c13b1a28b 100644 --- a/tracker/tracker-fetch/package.json +++ b/tracker/tracker-fetch/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-fetch", "description": "Tracker plugin for fetch requests recording ", - "version": "3.5.2", + "version": "3.5.3", "keywords": [ "fetch", "logging", diff --git a/tracker/tracker-fetch/src/index.ts b/tracker/tracker-fetch/src/index.ts index 93e39cce5..922913923 100644 --- a/tracker/tracker-fetch/src/index.ts +++ b/tracker/tracker-fetch/src/index.ts @@ -1,38 +1,48 @@ import { App, Messages } from '@openreplay/tracker'; -interface Request { - url: string, - body: string | Object, - headers: Record, +interface RequestData { + body: BodyInit | null | undefined + headers: Record } -interface Response { - url: string, - status: number, - body: string, - headers: Record, +interface ResponseData { + body: string | Object | null + headers: Record } +interface RequestResponseData { + readonly status: number + readonly method: string + url: string + request: RequestData + response: ResponseData +} + + export interface Options { - sessionTokenHeader?: string; - replaceDefault: boolean; // overrideDefault ? - failuresOnly: boolean; - ignoreHeaders: Array | boolean; - requestSanitizer: ((Request) => Request | null) | null; - responseSanitizer: ((Response) => Response | null) | null; + sessionTokenHeader?: string + failuresOnly: boolean + overrideGlobal: boolean + ignoreHeaders: Array | boolean + sanitiser?: (RequestResponseData) => RequestResponseData | null + + requestSanitizer?: any + responseSanitizer?: any } export default function(opts: Partial = {}) { const options: Options = Object.assign( { - replaceDefault: false, + overrideGlobal: false, failuresOnly: false, ignoreHeaders: [ 'Cookie', 'Set-Cookie', 'Authorization' ], - requestSanitizer: null, - responseSanitizer: null, }, opts, ); + if (options.requestSanitizer && options.responseSanitizer) { + console.warn("OpenReplay fetch plugin: `requestSanitizer` and `responseSanitizer` options are depricated. Please, use `sanitiser` instead (check out documentation at https://docs.openreplay.com/plugins/fetch).") + } + const origFetch = window.fetch return (app: App | null) => { if (app === null) { @@ -90,56 +100,55 @@ export default function(opts: Partial = {}) { r.headers.forEach((v, n) => { if (!isHIgnoring(n)) resHs[n] = v }) } - // Request forming - let reqBody = '' - if (typeof init.body === 'string') { - reqBody = init.body - } else if (typeof init.body === 'object') { - try { - reqBody = JSON.stringify(init.body) - } catch {} - } - let req: Request | null = { - url: input, + const req: RequestData = { headers: reqHs, - body: reqBody, - } - if (options.requestSanitizer !== null) { - req = options.requestSanitizer(req) - if (!req) { - return - } + body: init.body, } // Response forming - let res: Response | null = { - url: input, - status: r.status, + const res: ResponseData = { headers: resHs, body: text, } - if (options.responseSanitizer !== null) { - res = options.responseSanitizer(res) - if (!res) { + + const method = typeof init.method === 'string' + ? init.method.toUpperCase() + : 'GET' + let reqResInfo: RequestResponseData | null = { + url: input, + method, + status: r.status, + request: req, + response: res, + } + if (options.sanitiser) { + try { + reqResInfo.response.body = JSON.parse(text) as Object // Why the returning type is "any"? + } catch {} + reqResInfo = options.sanitiser(reqResInfo) + if (!reqResInfo) { return } } - const reqStr = JSON.stringify({ - headers: req.headers, - body: req.body, - }) - const resStr = JSON.stringify({ - headers: res.headers, - body: res.body, - }) + const getStj = (r: RequestData | ResponseData): string => { + if (r && typeof r.body !== 'string') { + try { + r.body = JSON.stringify(r.body) + } catch { + r.body = "" + //app.log.warn("Openreplay fetch") // TODO: version check + } + } + return JSON.stringify(r) + } app.send( Messages.Fetch( - typeof init.method === 'string' ? init.method.toUpperCase() : 'GET', - input, - reqStr, - resStr, + method, + String(reqResInfo.url), + getStj(reqResInfo.request), + getStj(reqResInfo.response), r.status, startTime + performance.timing.navigationStart, duration, @@ -147,8 +156,9 @@ export default function(opts: Partial = {}) { ) }); return response; - }; - if (options.replaceDefault) { + } + + if (options.overrideGlobal) { window.fetch = fetch } return fetch; From 3b3015e025225ae0ed85ee65981daef8675f385c Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Sun, 3 Apr 2022 13:31:54 +0200 Subject: [PATCH 039/294] fix(backend-http): always Close() body --- backend/services/http/handlers-ios.go | 4 ++-- backend/services/http/handlers-web.go | 2 +- backend/services/http/handlers.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/services/http/handlers-ios.go b/backend/services/http/handlers-ios.go index f15a6af60..8116980e1 100644 --- a/backend/services/http/handlers-ios.go +++ b/backend/services/http/handlers-ios.go @@ -50,7 +50,7 @@ func startSessionHandlerIOS(w http.ResponseWriter, r *http.Request) { startTime := time.Now() req := &request{} body := http.MaxBytesReader(w, r.Body, JSON_SIZE_LIMIT) - //defer body.Close() + defer body.Close() if err := json.NewDecoder(body).Decode(req); err != nil { responseWithError(w, http.StatusBadRequest, err) return @@ -155,7 +155,7 @@ func imagesUploadHandlerIOS(w http.ResponseWriter, r *http.Request) { } r.Body = http.MaxBytesReader(w, r.Body, FILES_SIZE_LIMIT) - // defer r.Body.Close() + defer r.Body.Close() err = r.ParseMultipartForm(1e6) // ~1Mb if err == http.ErrNotMultipart || err == http.ErrMissingBoundary { responseWithError(w, http.StatusUnsupportedMediaType, err) diff --git a/backend/services/http/handlers-web.go b/backend/services/http/handlers-web.go index 29dcf161d..dcbd33720 100644 --- a/backend/services/http/handlers-web.go +++ b/backend/services/http/handlers-web.go @@ -41,7 +41,7 @@ func startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) { startTime := time.Now() req := &request{} body := http.MaxBytesReader(w, r.Body, JSON_SIZE_LIMIT) // what if Body == nil?? // use r.ContentLength to return specific error? - //defer body.Close() + defer body.Close() if err := json.NewDecoder(body).Decode(req); err != nil { responseWithError(w, http.StatusBadRequest, err) return diff --git a/backend/services/http/handlers.go b/backend/services/http/handlers.go index e45e84e64..dd73925af 100644 --- a/backend/services/http/handlers.go +++ b/backend/services/http/handlers.go @@ -9,11 +9,11 @@ import ( gzip "github.com/klauspost/pgzip" ) -const JSON_SIZE_LIMIT int64 = 1e3 // 1Kb +const JSON_SIZE_LIMIT int64 = 1e3 // 1Kb func pushMessages(w http.ResponseWriter, r *http.Request, sessionID uint64, topicName string) { body := http.MaxBytesReader(w, r.Body, BEACON_SIZE_LIMIT) - //defer body.Close() + defer body.Close() var reader io.ReadCloser var err error switch r.Header.Get("Content-Encoding") { From 642618046ad44e8c65ef067ed293c9f1e4faf464 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 4 Apr 2022 10:40:44 +0200 Subject: [PATCH 040/294] 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 041/294] 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 72f2a96f31eda3129fd500706bc4cd145081bb49 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 4 Apr 2022 14:09:49 +0200 Subject: [PATCH 042/294] parrot global changes --- api/chalicelib/core/telemetry.py | 4 ++-- ee/api/chalicelib/core/telemetry.py | 4 ++-- ee/backend/pkg/license/check.go | 2 +- scripts/helm/install.sh | 2 +- scripts/helm/roles/openreplay/tasks/pre-check.yaml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/chalicelib/core/telemetry.py b/api/chalicelib/core/telemetry.py index 48f403f57..28eb97f73 100644 --- a/api/chalicelib/core/telemetry.py +++ b/api/chalicelib/core/telemetry.py @@ -30,7 +30,7 @@ def compute(): RETURNING *,(SELECT email FROM public.users WHERE role='owner' LIMIT 1);""" ) data = cur.fetchone() - requests.post('https://parrot.asayer.io/os/telemetry', json={"stats": [process_data(data)]}) + requests.post('https://api.openreplay.com/os/telemetry', json={"stats": [process_data(data)]}) def new_client(): @@ -40,4 +40,4 @@ def new_client(): (SELECT email FROM public.users WHERE role='owner' LIMIT 1) AS email FROM public.tenants;""") data = cur.fetchone() - requests.post('https://parrot.asayer.io/os/signup', json=process_data(data)) + requests.post('https://api.openreplay.com/os/signup', json=process_data(data)) diff --git a/ee/api/chalicelib/core/telemetry.py b/ee/api/chalicelib/core/telemetry.py index d9843e37d..e05df7fdc 100644 --- a/ee/api/chalicelib/core/telemetry.py +++ b/ee/api/chalicelib/core/telemetry.py @@ -53,7 +53,7 @@ def compute(): RETURNING *,(SELECT email FROM users_ee WHERE role = 'owner' AND users_ee.tenant_id = tenants.tenant_id LIMIT 1);""" ) data = cur.fetchall() - requests.post('https://parrot.asayer.io/os/telemetry', + requests.post('https://api.openreplay.com/os/telemetry', json={"stats": [process_data(d, edition='ee') for d in data]}) @@ -65,4 +65,4 @@ def new_client(tenant_id): FROM public.tenants WHERE tenant_id=%(tenant_id)s;""", {"tenant_id": tenant_id})) data = cur.fetchone() - requests.post('https://parrot.asayer.io/os/signup', json=process_data(data, edition='ee')) \ No newline at end of file + requests.post('https://api.openreplay.com/os/signup', json=process_data(data, edition='ee')) \ No newline at end of file diff --git a/ee/backend/pkg/license/check.go b/ee/backend/pkg/license/check.go index 6b33a625e..771558946 100644 --- a/ee/backend/pkg/license/check.go +++ b/ee/backend/pkg/license/check.go @@ -33,7 +33,7 @@ func CheckLicense() { log.Fatal("Can not form a license check request.") } - resp, err := http.Post("https://parrot.asayer.io/os/license", "application/json", bytes.NewReader(requestBody)) + resp, err := http.Post("https://api.openreplay.com/os/license", "application/json", bytes.NewReader(requestBody)) if err != nil { log.Fatalf("Error while checking license. %v", err) } diff --git a/scripts/helm/install.sh b/scripts/helm/install.sh index 529d08a8c..2bf39988c 100755 --- a/scripts/helm/install.sh +++ b/scripts/helm/install.sh @@ -26,7 +26,7 @@ which docker &> /dev/null || { } -# https://parrot.asayer.io/os/license +# https://api.openreplay.com/os/license # payload: {"mid": "UUID of the machine", "license": ""} # response {"data":{"valid": TRUE|FALSE, "expiration": expiration date in ms}} diff --git a/scripts/helm/roles/openreplay/tasks/pre-check.yaml b/scripts/helm/roles/openreplay/tasks/pre-check.yaml index 7f85d0ea1..90e6021c8 100644 --- a/scripts/helm/roles/openreplay/tasks/pre-check.yaml +++ b/scripts/helm/roles/openreplay/tasks/pre-check.yaml @@ -108,7 +108,7 @@ - all - name: Checking Enterprise Licence uri: - url: https://parrot.asayer.io/os/license + url: https://api.openreplay.com/os/license body: mid: "UUID of the machine" license: "{{ enterprise_edition_license }}" From 2b55aef0a782af5e4eba1f17af30bf8f668572ef Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 4 Apr 2022 14:29:49 +0200 Subject: [PATCH 043/294] feat(api): optimized boarding endpoints --- api/chalicelib/core/boarding.py | 86 +++++++++++++++--------------- api/chalicelib/core/projects.py | 10 ++++ ee/api/chalicelib/core/boarding.py | 86 +++++++++++++++--------------- ee/api/chalicelib/core/projects.py | 12 ++++- 4 files changed, 105 insertions(+), 89 deletions(-) diff --git a/api/chalicelib/core/boarding.py b/api/chalicelib/core/boarding.py index c303643c8..68843b2f8 100644 --- a/api/chalicelib/core/boarding.py +++ b/api/chalicelib/core/boarding.py @@ -5,39 +5,38 @@ from chalicelib.core import users def get_state(tenant_id): - my_projects = projects.get_projects(tenant_id=tenant_id, recording_state=False) - pids = [s["projectId"] for s in my_projects] + pids = projects.get_projects_ids(tenant_id=tenant_id) with pg_client.PostgresClient() as cur: recorded = False meta = False if len(pids) > 0: cur.execute( - cur.mogrify("""\ - SELECT - COUNT(*) - FROM public.sessions AS s - where s.project_id IN %(ids)s - LIMIT 1;""", + cur.mogrify("""SELECT EXISTS(( SELECT 1 + FROM public.sessions AS s + WHERE s.project_id IN %(ids)s)) AS exists;""", {"ids": tuple(pids)}) ) - recorded = cur.fetchone()["count"] > 0 + recorded = cur.fetchone()["exists"] meta = False if recorded: - cur.execute("""SELECT SUM((SELECT COUNT(t.meta) - FROM (VALUES (p.metadata_1), (p.metadata_2), (p.metadata_3), (p.metadata_4), (p.metadata_5), - (p.metadata_6), (p.metadata_7), (p.metadata_8), (p.metadata_9), (p.metadata_10), - (sessions.user_id)) AS t(meta) - WHERE t.meta NOTNULL)) - FROM public.projects AS p - LEFT JOIN LATERAL ( SELECT 'defined' - FROM public.sessions - WHERE sessions.project_id=p.project_id AND sessions.user_id IS NOT NULL - LIMIT 1) AS sessions(user_id) ON(TRUE) - WHERE p.deleted_at ISNULL;""" - ) + cur.execute("""SELECT EXISTS((SELECT 1 + FROM public.projects AS p + LEFT JOIN LATERAL ( SELECT 1 + FROM public.sessions + WHERE sessions.project_id = p.project_id + AND sessions.user_id IS NOT NULL + LIMIT 1) AS sessions(user_id) ON (TRUE) + WHERE p.deleted_at ISNULL + AND ( sessions.user_id IS NOT NULL OR p.metadata_1 IS NOT NULL + OR p.metadata_2 IS NOT NULL OR p.metadata_3 IS NOT NULL + OR p.metadata_4 IS NOT NULL OR p.metadata_5 IS NOT NULL + OR p.metadata_6 IS NOT NULL OR p.metadata_7 IS NOT NULL + OR p.metadata_8 IS NOT NULL OR p.metadata_9 IS NOT NULL + OR p.metadata_10 IS NOT NULL ) + )) AS exists;""") - meta = cur.fetchone()["sum"] > 0 + meta = cur.fetchone()["exists"] return [ {"task": "Install OpenReplay", @@ -58,22 +57,18 @@ def get_state(tenant_id): def get_state_installing(tenant_id): - my_projects = projects.get_projects(tenant_id=tenant_id, recording_state=False) - pids = [s["projectId"] for s in my_projects] + pids = projects.get_projects_ids(tenant_id=tenant_id) with pg_client.PostgresClient() as cur: recorded = False if len(pids) > 0: cur.execute( - cur.mogrify("""\ - SELECT - COUNT(*) - FROM public.sessions AS s - where s.project_id IN %(ids)s - LIMIT 1;""", + cur.mogrify("""SELECT EXISTS(( SELECT 1 + FROM public.sessions AS s + WHERE s.project_id IN %(ids)s)) AS exists;""", {"ids": tuple(pids)}) ) - recorded = cur.fetchone()["count"] > 0 + recorded = cur.fetchone()["exists"] return {"task": "Install OpenReplay", "done": recorded, @@ -82,20 +77,23 @@ def get_state_installing(tenant_id): def get_state_identify_users(tenant_id): with pg_client.PostgresClient() as cur: - cur.execute( - """SELECT SUM((SELECT COUNT(t.meta) - FROM (VALUES (p.metadata_1), (p.metadata_2), (p.metadata_3), (p.metadata_4), (p.metadata_5), - (p.metadata_6), (p.metadata_7), (p.metadata_8), (p.metadata_9), (p.metadata_10), - (sessions.user_id)) AS t(meta) - WHERE t.meta NOTNULL)) - FROM public.projects AS p - LEFT JOIN LATERAL ( SELECT 'defined' - FROM public.sessions - WHERE sessions.project_id=p.project_id AND sessions.user_id IS NOT NULL - LIMIT 1) AS sessions(user_id) ON(TRUE) - WHERE p.deleted_at ISNULL;""") + cur.execute("""SELECT EXISTS((SELECT 1 + FROM public.projects AS p + LEFT JOIN LATERAL ( SELECT 1 + FROM public.sessions + WHERE sessions.project_id = p.project_id + AND sessions.user_id IS NOT NULL + LIMIT 1) AS sessions(user_id) ON (TRUE) + WHERE p.deleted_at ISNULL + AND ( sessions.user_id IS NOT NULL OR p.metadata_1 IS NOT NULL + OR p.metadata_2 IS NOT NULL OR p.metadata_3 IS NOT NULL + OR p.metadata_4 IS NOT NULL OR p.metadata_5 IS NOT NULL + OR p.metadata_6 IS NOT NULL OR p.metadata_7 IS NOT NULL + OR p.metadata_8 IS NOT NULL OR p.metadata_9 IS NOT NULL + OR p.metadata_10 IS NOT NULL ) + )) AS exists;""") - meta = cur.fetchone()["sum"] > 0 + meta = cur.fetchone()["exists"] return {"task": "Identify Users", "done": meta, diff --git a/api/chalicelib/core/projects.py b/api/chalicelib/core/projects.py index c5ae912aa..c57360dc4 100644 --- a/api/chalicelib/core/projects.py +++ b/api/chalicelib/core/projects.py @@ -280,3 +280,13 @@ def update_capture_status(project_id, changes): ) return changes + + +def get_projects_ids(tenant_id): + with pg_client.PostgresClient() as cur: + cur.execute(f"""SELECT s.project_id + FROM public.projects AS s + WHERE s.deleted_at IS NULL + ORDER BY s.project_id;""") + rows = cur.fetchall() + return [r["project_id"] for r in rows] diff --git a/ee/api/chalicelib/core/boarding.py b/ee/api/chalicelib/core/boarding.py index 6690e59f2..8a2076b58 100644 --- a/ee/api/chalicelib/core/boarding.py +++ b/ee/api/chalicelib/core/boarding.py @@ -6,41 +6,40 @@ from chalicelib.core import projects def get_state(tenant_id): - my_projects = projects.get_projects(tenant_id=tenant_id, recording_state=False) - pids = [s["projectId"] for s in my_projects] + pids = projects.get_projects_ids(tenant_id=tenant_id) with pg_client.PostgresClient() as cur: recorded = False meta = False if len(pids) > 0: cur.execute( - cur.mogrify("""\ - SELECT - COUNT(*) - FROM public.sessions AS s - where s.project_id IN %(ids)s - LIMIT 1;""", + cur.mogrify("""SELECT EXISTS(( SELECT 1 + FROM public.sessions AS s + WHERE s.project_id IN %(ids)s)) AS exists;""", {"ids": tuple(pids)}) ) - recorded = cur.fetchone()["count"] > 0 + recorded = cur.fetchone()["exists"] meta = False if recorded: cur.execute( - cur.mogrify("""SELECT SUM((SELECT COUNT(t.meta) - FROM (VALUES (p.metadata_1), (p.metadata_2), (p.metadata_3), (p.metadata_4), (p.metadata_5), - (p.metadata_6), (p.metadata_7), (p.metadata_8), (p.metadata_9), (p.metadata_10), - (sessions.user_id)) AS t(meta) - WHERE t.meta NOTNULL)) - FROM public.projects AS p - LEFT JOIN LATERAL ( SELECT 'defined' - FROM public.sessions - WHERE sessions.project_id=p.project_id AND sessions.user_id IS NOT NULL - LIMIT 1) AS sessions(user_id) ON(TRUE) - WHERE p.tenant_id = %(tenant_id)s - AND p.deleted_at ISNULL;""" + cur.mogrify("""SELECT EXISTS((SELECT 1 + FROM public.projects AS p + LEFT JOIN LATERAL ( SELECT 1 + FROM public.sessions + WHERE sessions.project_id = p.project_id + AND sessions.user_id IS NOT NULL + LIMIT 1) AS sessions(user_id) ON (TRUE) + WHERE p.tenant_id = %(tenant_id)s AND p.deleted_at ISNULL + AND ( sessions.user_id IS NOT NULL OR p.metadata_1 IS NOT NULL + OR p.metadata_2 IS NOT NULL OR p.metadata_3 IS NOT NULL + OR p.metadata_4 IS NOT NULL OR p.metadata_5 IS NOT NULL + OR p.metadata_6 IS NOT NULL OR p.metadata_7 IS NOT NULL + OR p.metadata_8 IS NOT NULL OR p.metadata_9 IS NOT NULL + OR p.metadata_10 IS NOT NULL ) + )) AS exists;""" , {"tenant_id": tenant_id})) - meta = cur.fetchone()["sum"] > 0 + meta = cur.fetchone()["exists"] return [ {"task": "Install OpenReplay", @@ -61,22 +60,18 @@ def get_state(tenant_id): def get_state_installing(tenant_id): - my_projects = projects.get_projects(tenant_id=tenant_id, recording_state=False) - pids = [s["projectId"] for s in my_projects] + pids = projects.get_projects_ids(tenant_id=tenant_id) with pg_client.PostgresClient() as cur: recorded = False if len(pids) > 0: cur.execute( - cur.mogrify("""\ - SELECT - COUNT(*) - FROM public.sessions AS s - where s.project_id IN %(ids)s - LIMIT 1;""", + cur.mogrify("""SELECT EXISTS(( SELECT 1 + FROM public.sessions AS s + WHERE s.project_id IN %(ids)s)) AS exists;""", {"ids": tuple(pids)}) ) - recorded = cur.fetchone()["count"] > 0 + recorded = cur.fetchone()["exists"] return {"task": "Install OpenReplay", "done": recorded, @@ -86,21 +81,24 @@ def get_state_installing(tenant_id): def get_state_identify_users(tenant_id): with pg_client.PostgresClient() as cur: cur.execute( - cur.mogrify("""SELECT SUM((SELECT COUNT(t.meta) - FROM (VALUES (p.metadata_1), (p.metadata_2), (p.metadata_3), (p.metadata_4), (p.metadata_5), - (p.metadata_6), (p.metadata_7), (p.metadata_8), (p.metadata_9), (p.metadata_10), - (sessions.user_id)) AS t(meta) - WHERE t.meta NOTNULL)) - FROM public.projects AS p - LEFT JOIN LATERAL ( SELECT 'defined' - FROM public.sessions - WHERE sessions.project_id=p.project_id AND sessions.user_id IS NOT NULL - LIMIT 1) AS sessions(user_id) ON(TRUE) - WHERE p.tenant_id = %(tenant_id)s - AND p.deleted_at ISNULL;""" + cur.mogrify("""SELECT EXISTS((SELECT 1 + FROM public.projects AS p + LEFT JOIN LATERAL ( SELECT 1 + FROM public.sessions + WHERE sessions.project_id = p.project_id + AND sessions.user_id IS NOT NULL + LIMIT 1) AS sessions(user_id) ON (TRUE) + WHERE p.tenant_id = %(tenant_id)s AND p.deleted_at ISNULL + AND ( sessions.user_id IS NOT NULL OR p.metadata_1 IS NOT NULL + OR p.metadata_2 IS NOT NULL OR p.metadata_3 IS NOT NULL + OR p.metadata_4 IS NOT NULL OR p.metadata_5 IS NOT NULL + OR p.metadata_6 IS NOT NULL OR p.metadata_7 IS NOT NULL + OR p.metadata_8 IS NOT NULL OR p.metadata_9 IS NOT NULL + OR p.metadata_10 IS NOT NULL ) + )) AS exists;""" , {"tenant_id": tenant_id})) - meta = cur.fetchone()["sum"] > 0 + meta = cur.fetchone()["exists"] return {"task": "Identify Users", "done": meta, diff --git a/ee/api/chalicelib/core/projects.py b/ee/api/chalicelib/core/projects.py index 75a3a31d0..0255c8c8c 100644 --- a/ee/api/chalicelib/core/projects.py +++ b/ee/api/chalicelib/core/projects.py @@ -324,7 +324,7 @@ def is_authorized_batch(project_ids, tenant_id): query = cur.mogrify("""\ SELECT project_id FROM public.projects - where tenant_id =%(tenant_id)s + WHERE tenant_id =%(tenant_id)s AND project_id IN %(project_ids)s AND deleted_at IS NULL;""", {"tenant_id": tenant_id, "project_ids": tuple(project_ids)}) @@ -334,3 +334,13 @@ def is_authorized_batch(project_ids, tenant_id): ) rows = cur.fetchall() return [r["project_id"] for r in rows] + + +def get_projects_ids(tenant_id): + with pg_client.PostgresClient() as cur: + cur.execute(cur.mogrify("""SELECT s.project_id + FROM public.projects AS s + WHERE tenant_id =%(tenant_id)s AND s.deleted_at IS NULL + ORDER BY s.project_id;""", {"tenant_id": tenant_id})) + rows = cur.fetchall() + return [r["project_id"] for r in rows] From 6360712aebe2c5f129693aaf890fa874e9883223 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 4 Apr 2022 15:21:55 +0200 Subject: [PATCH 044/294] 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 045/294] 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 e75f7ddb4fb5e24dc7a820e352f3e4c2b6d73a1e Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 4 Apr 2022 16:36:51 +0200 Subject: [PATCH 046/294] feat(api): fixed sort sessions --- api/chalicelib/core/sessions.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/api/chalicelib/core/sessions.py b/api/chalicelib/core/sessions.py index 1903cc08b..8f619f66b 100644 --- a/api/chalicelib/core/sessions.py +++ b/api/chalicelib/core/sessions.py @@ -233,20 +233,19 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, e data.order = "DESC" sort = 'session_id' if data.sort is not None and data.sort != "session_id": - sort += " " + data.order + "," + helper.key_to_snake_case(data.sort) - else: - sort = 'session_id' + # sort += " " + data.order + "," + helper.key_to_snake_case(data.sort) + sort = helper.key_to_snake_case(data.sort) meta_keys = metadata.get(project_id=project_id) main_query = cur.mogrify(f"""SELECT COUNT(full_sessions) AS count, COALESCE(JSONB_AGG(full_sessions) FILTER (WHERE rn>%(sessions_limit_s)s AND rn<=%(sessions_limit_e)s), '[]'::JSONB) AS sessions - FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY issue_score DESC, {sort} {data.order}, session_id desc) AS rn + FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY {sort} {data.order}, issue_score DESC) AS rn FROM (SELECT DISTINCT ON(s.session_id) {SESSION_PROJECTION_COLS} {"," if len(meta_keys) > 0 else ""}{",".join([f'metadata_{m["index"]}' for m in meta_keys])} {query_part} ORDER BY s.session_id desc) AS filtred_sessions - ORDER BY issue_score DESC, {sort} {data.order}) AS full_sessions;""", + ORDER BY {sort} {data.order}, issue_score DESC) AS full_sessions;""", full_args) # print("--------------------") # print(main_query) @@ -280,9 +279,9 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, e for i, s in enumerate(sessions): sessions[i]["metadata"] = {k["key"]: sessions[i][f'metadata_{k["index"]}'] for k in meta_keys \ if sessions[i][f'metadata_{k["index"]}'] is not None} - if not data.group_by_user and data.sort is not None and data.sort != "session_id": - sessions = sorted(sessions, key=lambda s: s[helper.key_to_snake_case(data.sort)], - reverse=data.order.upper() == "DESC") + # if not data.group_by_user and data.sort is not None and data.sort != "session_id": + # sessions = sorted(sessions, key=lambda s: s[helper.key_to_snake_case(data.sort)], + # reverse=data.order.upper() == "DESC") return { 'total': total, 'sessions': helper.list_to_camel_case(sessions) From 011cbd006bb3ae5cc1556bb52fb6bf87b2dc8074 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Mon, 4 Apr 2022 20:40:10 +0200 Subject: [PATCH 047/294] fix(ackend-assets): use cachePath in timeout map instead of original url --- backend/services/assets/cacher/cacher.go | 47 ++++++++++---------- backend/services/assets/cacher/timeoutMap.go | 12 ++--- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/backend/services/assets/cacher/cacher.go b/backend/services/assets/cacher/cacher.go index 59b09449f..70ea31928 100644 --- a/backend/services/assets/cacher/cacher.go +++ b/backend/services/assets/cacher/cacher.go @@ -1,31 +1,31 @@ package cacher import ( + "crypto/tls" "fmt" "io" "io/ioutil" "mime" "net/http" - "crypto/tls" "path/filepath" "strings" "time" "github.com/pkg/errors" - - "openreplay/backend/pkg/url/assets" + "openreplay/backend/pkg/storage" + "openreplay/backend/pkg/url/assets" ) const MAX_CACHE_DEPTH = 5 type cacher struct { - timeoutMap *timeoutMap // Concurrency implemented - s3 *storage.S3 // AWS Docs: "These clients are safe to use concurrently." - httpClient *http.Client // Docs: "Clients are safe for concurrent use by multiple goroutines." - rewriter *assets.Rewriter // Read only - Errors chan error - sizeLimit int + timeoutMap *timeoutMap // Concurrency implemented + s3 *storage.S3 // AWS Docs: "These clients are safe to use concurrently." + httpClient *http.Client // Docs: "Clients are safe for concurrent use by multiple goroutines." + rewriter *assets.Rewriter // Read only + Errors chan error + sizeLimit int } func NewCacher(region string, bucket string, origin string, sizeLimit int) *cacher { @@ -36,26 +36,26 @@ func NewCacher(region string, bucket string, origin string, sizeLimit int) *cach httpClient: &http.Client{ Timeout: time.Duration(6) * time.Second, Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, }, - rewriter: rewriter, - Errors: make(chan error), - sizeLimit: sizeLimit, + rewriter: rewriter, + Errors: make(chan error), + sizeLimit: sizeLimit, } } func (c *cacher) cacheURL(requestURL string, sessionID uint64, depth byte, context string, isJS bool) { - if c.timeoutMap.contains(requestURL) { - return - } - c.timeoutMap.add(requestURL) var cachePath string - if (isJS) { + if isJS { cachePath = assets.GetCachePathForJS(requestURL) } else { cachePath = assets.GetCachePathForAssets(sessionID, requestURL) } + if c.timeoutMap.contains(cachePath) { + return + } + c.timeoutMap.add(cachePath) if c.s3.Exists(cachePath) { return } @@ -94,20 +94,19 @@ func (c *cacher) cacheURL(requestURL string, sessionID uint64, depth byte, conte if isCSS { strData = c.rewriter.RewriteCSS(sessionID, requestURL, strData) // TODO: one method for reqrite and return list } - - // TODO: implement in streams + + // TODO: implement in streams err = c.s3.Upload(strings.NewReader(strData), cachePath, contentType, false) if err != nil { c.Errors <- errors.Wrap(err, context) return } - c.timeoutMap.add(requestURL) if isCSS { if depth > 0 { for _, extractedURL := range assets.ExtractURLsFromCSS(string(data)) { - if fullURL, cachable := assets.GetFullCachableURL(requestURL, extractedURL); cachable { - go c.cacheURL(fullURL, sessionID, depth-1, context + "\n -> " + fullURL, false) + if fullURL, cachable := assets.GetFullCachableURL(requestURL, extractedURL); cachable { + go c.cacheURL(fullURL, sessionID, depth-1, context+"\n -> "+fullURL, false) } } if err != nil { diff --git a/backend/services/assets/cacher/timeoutMap.go b/backend/services/assets/cacher/timeoutMap.go index 36fc4ee4d..5a8e31424 100644 --- a/backend/services/assets/cacher/timeoutMap.go +++ b/backend/services/assets/cacher/timeoutMap.go @@ -5,30 +5,30 @@ import ( "time" ) -const MAX_STORAGE_TIME = 18 * time.Hour +const MAX_STORAGE_TIME = 24 * time.Hour // If problem with cache contention (>=4 core) look at sync.Map type timeoutMap struct { mx sync.RWMutex - m map[string]time.Time + m map[string]time.Time } func newTimeoutMap() *timeoutMap { return &timeoutMap{ m: make(map[string]time.Time), } -} +} func (tm *timeoutMap) add(key string) { tm.mx.Lock() - defer tm.mx.Unlock() + defer tm.mx.Unlock() tm.m[key] = time.Now() } func (tm *timeoutMap) contains(key string) bool { tm.mx.RLock() - defer tm.mx.RUnlock() + defer tm.mx.RUnlock() _, ok := tm.m[key] return ok } @@ -36,7 +36,7 @@ func (tm *timeoutMap) contains(key string) bool { func (tm *timeoutMap) deleteOutdated() { now := time.Now() tm.mx.Lock() - defer tm.mx.Unlock() + defer tm.mx.Unlock() for key, t := range tm.m { if now.Sub(t) > MAX_STORAGE_TIME { delete(tm.m, key) From ef4c9a5bf5def1ccc8f09e3054217725a37aa726 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 5 Apr 2022 11:28:47 +0200 Subject: [PATCH 048/294] 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 049/294] 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 050/294] 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 051/294] 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 052/294] 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 053/294] 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 054/294] 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 055/294] 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 056/294] 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 057/294] 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 058/294] 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 059/294] 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 060/294] 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 061/294] 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 062/294] 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 063/294] 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 064/294] 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 065/294] 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 c30ceb447d54886a132b5f36b07fca749ab4fd0f Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 15:28:22 +0200 Subject: [PATCH 066/294] feat(utilities): FOSS-WS send SESSION_RECONNECTED on session's reconnection --- utilities/servers/websocket.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utilities/servers/websocket.js b/utilities/servers/websocket.js index 772bd7315..3cd22f3fc 100644 --- a/utilities/servers/websocket.js +++ b/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; @@ -258,6 +259,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) { From c9f7b6e98034b5472a915b62ddf441f9f9c33fb3 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 16:48:26 +0200 Subject: [PATCH 067/294] 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 068/294] 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 069/294] 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 4fa1751a68b518368319c80410669e81da41b547 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Wed, 6 Apr 2022 16:53:49 +0200 Subject: [PATCH 070/294] fix(backend-storage): dir clean in routine; MAX_POLL_INTERVAL_MS env var --- backend/Dockerfile | 1 + backend/services/storage/clean.go | 25 +++++++++++++------------ backend/services/storage/main.go | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 4c31518ef..b7a494f86 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -27,6 +27,7 @@ ENV TZ=UTC \ HTTP_PORT=80 \ BEACON_SIZE_LIMIT=7000000 \ KAFKA_USE_SSL=true \ + KAFKA_MAX_POLL_INTERVAL_MS=400000 \ REDIS_STREAMS_MAX_LEN=3000 \ TOPIC_RAW_WEB=raw \ TOPIC_RAW_IOS=raw-ios \ diff --git a/backend/services/storage/clean.go b/backend/services/storage/clean.go index 829bc8705..72f5f359c 100644 --- a/backend/services/storage/clean.go +++ b/backend/services/storage/clean.go @@ -1,23 +1,23 @@ package main import ( - "os" - "log" - "time" - "strconv" "io/ioutil" + "log" + "os" + "strconv" + "time" "openreplay/backend/pkg/flakeid" ) -const DELETE_TIMEOUT = 12 * time.Hour; +const DELETE_TIMEOUT = 48 * time.Hour func cleanDir(dirname string) { - files, err := ioutil.ReadDir(dirname) - if err != nil { - log.Printf("Cannot read file directory. %v", err) - return - } + files, err := ioutil.ReadDir(dirname) + if err != nil { + log.Printf("Cannot read file directory. %v", err) + return + } for _, f := range files { name := f.Name() @@ -27,8 +27,9 @@ func cleanDir(dirname string) { continue } ts := int64(flakeid.ExtractTimestamp(id)) - if time.Unix(ts/1000, 0).Add(DELETE_TIMEOUT).Before(time.Now()) { + if time.UnixMilli(ts).Add(DELETE_TIMEOUT).Before(time.Now()) { + // returns a error. Don't log it sinse it can be race condition between worker instances os.Remove(dirname + "/" + name) } } -} \ No newline at end of file +} diff --git a/backend/services/storage/main.go b/backend/services/storage/main.go index cd585f10e..9579fbe4f 100644 --- a/backend/services/storage/main.go +++ b/backend/services/storage/main.go @@ -69,7 +69,7 @@ func main() { consumer.Close() os.Exit(0) case <-cleanTick: - cleanDir(FS_DIR) + go cleanDir(FS_DIR) default: err := consumer.ConsumeNext() if err != nil { From 7e3597521c19b39bb367d7c13dc959a140ab8a2c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 6 Apr 2022 17:14:26 +0200 Subject: [PATCH 071/294] 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 072/294] 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 a04f8c3028ea3717cbf2102a64ba8f59685e6b8d Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Wed, 6 Apr 2022 20:13:43 +0200 Subject: [PATCH 073/294] fix(assist-frontend):handle session reconnect --- .../app/player/MessageDistributor/managers/AssistManager.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/app/player/MessageDistributor/managers/AssistManager.ts b/frontend/app/player/MessageDistributor/managers/AssistManager.ts index b684919f9..e888e18a4 100644 --- a/frontend/app/player/MessageDistributor/managers/AssistManager.ts +++ b/frontend/app/player/MessageDistributor/managers/AssistManager.ts @@ -144,7 +144,6 @@ export default class AssistManager { }) socket.on('messages', messages => { //console.log(messages.filter(m => m._id === 41 || m._id === 44)) - showDisconnectTimeout && clearTimeout(showDisconnectTimeout); jmr.append(messages) // as RawMessage[] if (waitingForMessages) { @@ -168,6 +167,9 @@ export default class AssistManager { socket.on("control_rejected", id => { id === socket.id && this.toggleRemoteControl(false) }) + socket.on('SESSION_RECONNECTED', () => { + showDisconnectTimeout && clearTimeout(showDisconnectTimeout) + }) socket.on('SESSION_DISCONNECTED', e => { waitingForMessages = true showDisconnectTimeout = setTimeout(() => { From c7d42553f23a77bd374bc4e0c0a76d0bf97669ae Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 7 Apr 2022 00:09:28 +0200 Subject: [PATCH 074/294] chore(minio): modularizing components Signed-off-by: rjshrjndrn --- scripts/helmcharts/openreplay/files/minio.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/helmcharts/openreplay/files/minio.sh b/scripts/helmcharts/openreplay/files/minio.sh index 1cc3e0460..903168b1b 100644 --- a/scripts/helmcharts/openreplay/files/minio.sh +++ b/scripts/helmcharts/openreplay/files/minio.sh @@ -12,9 +12,7 @@ mc alias set minio http://minio.db.svc.cluster.local:9000 $MINIO_ACCESS_KEY $MIN function init() { echo "Initializing minio" -for bucket in ${buckets[*]}; do -mc mb minio/${bucket} || true -mc ilm import minio/${bucket} < /tmp/lifecycle.json { "Rules": [ { @@ -27,13 +25,17 @@ mc ilm import minio/${bucket} < Date: Thu, 7 Apr 2022 11:29:40 +0200 Subject: [PATCH 075/294] 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 076/294] 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 077/294] 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 078/294] 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 079/294] 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 080/294] 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 081/294] 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 082/294] 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 083/294] 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 084/294] 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 085/294] 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 086/294] 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 087/294] 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 088/294] 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 089/294] 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 090/294] 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 091/294] 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 c6a5ec42369030805023fb07767997ae48d5744e Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Fri, 8 Apr 2022 11:39:23 +0200 Subject: [PATCH 092/294] chore(helm): update note Signed-off-by: rjshrjndrn --- .../helmcharts/openreplay/templates/NOTES.txt | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/scripts/helmcharts/openreplay/templates/NOTES.txt b/scripts/helmcharts/openreplay/templates/NOTES.txt index 6881933f1..1479a07c3 100644 --- a/scripts/helmcharts/openreplay/templates/NOTES.txt +++ b/scripts/helmcharts/openreplay/templates/NOTES.txt @@ -1,22 +1 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "openreplay.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "openreplay.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "openreplay.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "openreplay.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT -{{- end }} +OpenReplay Installation is complete. Follow along with the SSL configuration from [doc](https://docs.openreplay.com/deployment/deploy-aws#configuretls/ssl). From 351d1749e9b6c64e1ddea199371125ece61dc2e2 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 8 Apr 2022 16:43:55 +0200 Subject: [PATCH 093/294] 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 094/294] 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 095/294] 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 096/294] 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 097/294] 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 098/294] 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 3984b2325063bcfb067ea94f481764fcd7e2bd4b Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 8 Apr 2022 18:26:59 +0200 Subject: [PATCH 099/294] feat(ui) - dashboard - wip --- frontend/app/components/Modal/ModalOverlay.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/app/components/Modal/ModalOverlay.tsx b/frontend/app/components/Modal/ModalOverlay.tsx index 8660f53a4..85b314eec 100644 --- a/frontend/app/components/Modal/ModalOverlay.tsx +++ b/frontend/app/components/Modal/ModalOverlay.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { ModalContext } from "App/components/Modal/modalContext"; import { useModal } from 'App/components/Modal'; import stl from './ModalOverlay.css' From 7f806dda9338e87fe09aa0ebabfe229aa1ae8e92 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 8 Apr 2022 18:27:58 +0200 Subject: [PATCH 100/294] feat(ui) - dashboard - wip --- .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 101/294] 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)) From 1b0cc63e1f56d8ff9d0fa3e1fbae43fae487a9a5 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 8 Apr 2022 19:23:59 +0200 Subject: [PATCH 102/294] fix(ui) - saved search operator --- frontend/app/duck/search.js | 2 +- frontend/app/types/filter/newFilter.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index 9106227bb..f46f5a30a 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -71,7 +71,7 @@ function reducer(state = initialState, action = {}) { return state.set("instance", action.data); case success(FETCH_LIST): const { data } = action; - return state.set("list", List(data.map(SavedFilter))); + return state.set("list", List(data.map(SavedFilter)).sortBy(i => i.searchId)); case success(FETCH_FILTER_SEARCH): const groupedList = action.data.reduce((acc, item) => { const { projectId, type, value } = item; diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index 6963643f7..7a640bbbd 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -140,8 +140,8 @@ export default Record({ } } return { - ...filter, ..._filter, + ...filter, key: _filter.key, type: _filter.type, // camelCased(filter.type.toLowerCase()), value: value.length === 0 || !value ? [""] : value, From c550a6923debefd505525fa87be5be0072d15ddb Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 8 Apr 2022 19:34:36 +0200 Subject: [PATCH 103/294] fix(ui) - filter userid value on delete --- .../app/components/shared/Filters/FilterModal/FilterModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx b/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx index cb9c6f768..04688de6b 100644 --- a/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx +++ b/frontend/app/components/shared/Filters/FilterModal/FilterModal.tsx @@ -73,7 +73,7 @@ function FilterModal(props: Props) {
{key}
{filters[key].map((filter: any) => ( -
onFilterClick(filter)}> +
onFilterClick({ ...filter, value: [''] })}> {filter.label}
From 89b63cae273c4385a21b31071e94f3a0fa3fb1da Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Sun, 10 Apr 2022 15:53:24 +0200 Subject: [PATCH 104/294] feat-fix(tracker):3.5.5 * Fix behaviour when initialised inside iframe * Capture select element changes * Retry /i when response is not 200 (and not 401) * Capture initial element scroll on start * Fallback Label vallue to name, id or class --- tracker/tracker/package.json | 2 +- tracker/tracker/src/main/app/context.ts | 4 +- tracker/tracker/src/main/app/index.ts | 3 +- tracker/tracker/src/main/modules/input.ts | 38 ++++++++---- tracker/tracker/src/main/modules/mouse.ts | 49 +++++++++------- tracker/tracker/src/main/modules/scroll.ts | 12 ++-- tracker/tracker/src/webworker/index.ts | 67 +++++++++++----------- 7 files changed, 101 insertions(+), 74 deletions(-) diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 6df50033b..9c7920ad7 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "3.5.4", + "version": "3.5.5", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/main/app/context.ts b/tracker/tracker/src/main/app/context.ts index 781f91ea8..fd7ec11dd 100644 --- a/tracker/tracker/src/main/app/context.ts +++ b/tracker/tracker/src/main/app/context.ts @@ -41,13 +41,13 @@ export function isInstance(node: Node, constr: Cons // @ts-ignore (for EI, Safary) doc.parentWindow || doc.defaultView; // TODO: smart global typing for Window object - while((context.parent || context.top) && context.parent !== context) { + while(context !== window) { // @ts-ignore if (node instanceof context[constr.name]) { return true } // @ts-ignore - context = context.parent || context.top + context = context.parent || window } // @ts-ignore return node instanceof context[constr.name] diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts index 59129cbec..de9518af6 100644 --- a/tracker/tracker/src/main/app/index.ts +++ b/tracker/tracker/src/main/app/index.ts @@ -234,10 +234,11 @@ export default class App { ); } + // TODO: full correct semantic checkRequiredVersion(version: string): boolean { const reqVer = version.split('.') const ver = this.version.split('.') - for (let i = 0; i < ver.length; i++) { + for (let i = 0; i < 3; i++) { if (Number(ver[i]) < Number(reqVer[i]) || isNaN(Number(ver[i])) || isNaN(Number(reqVer[i]))) { return false } diff --git a/tracker/tracker/src/main/modules/input.ts b/tracker/tracker/src/main/modules/input.ts index ad8cda673..0cad3c58b 100644 --- a/tracker/tracker/src/main/modules/input.ts +++ b/tracker/tracker/src/main/modules/input.ts @@ -1,4 +1,9 @@ -import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute } from "../utils.js"; +import { + normSpaces, + IN_BROWSER, + getLabelAttribute, + hasOpenreplayAttribute, +} from "../utils.js"; import App from "../app/index.js"; import { SetInputTarget, SetInputValue, SetInputChecked } from "../../messages/index.js"; @@ -31,14 +36,14 @@ function isCheckable(node: any): node is HTMLInputElement { } const labelElementFor: ( - node: TextEditableElement, + element: TextEditableElement, ) => HTMLLabelElement | undefined = IN_BROWSER && 'labels' in HTMLInputElement.prototype - ? (node): HTMLLabelElement | undefined => { + ? (node) => { let p: Node | null = node; while ((p = p.parentNode) !== null) { - if (p.nodeName === 'LABEL') { - return p as HTMLLabelElement; + if (p instanceof HTMLLabelElement) { + return p } } const labels = node.labels; @@ -46,10 +51,10 @@ const labelElementFor: ( return labels[0]; } } - : (node): HTMLLabelElement | undefined => { + : (node) => { let p: Node | null = node; while ((p = p.parentNode) !== null) { - if (p.nodeName === 'LABEL') { + if (p instanceof HTMLLabelElement) { return p as HTMLLabelElement; } } @@ -66,10 +71,12 @@ export function getInputLabel(node: TextEditableElement): string { let label = getLabelAttribute(node); if (label === null) { const labelElement = labelElementFor(node); - label = - labelElement === undefined - ? node.placeholder || node.name - : labelElement.innerText; + label = (labelElement && labelElement.innerText) + || node.placeholder + || node.name + || node.id + || node.className + || node.type } return normSpaces(label).slice(0, 100); } @@ -101,7 +108,7 @@ export default function (app: App, opts: Partial): void { app.send(new SetInputTarget(id, label)); } } - function sendInputValue(id: number, node: TextEditableElement): void { + function sendInputValue(id: number, node: TextEditableElement | HTMLSelectElement): void { let value = node.value; let inputMode: InputMode = options.defaultInputMode; if (node.type === 'password' || hasOpenreplayAttribute(node, 'hidden')) { @@ -175,6 +182,13 @@ export default function (app: App, opts: Partial): void { if (id === undefined) { return; } + // TODO: support multiple select (?): use selectedOptions; Need send target? + if (node instanceof HTMLSelectElement) { + sendInputValue(id, node) + app.attachEventListener(node, "change", () => { + sendInputValue(id, node) + }) + } if (isTextEditable(node)) { inputValues.set(id, node.value); sendInputValue(id, node); diff --git a/tracker/tracker/src/main/modules/mouse.ts b/tracker/tracker/src/main/modules/mouse.ts index 90cf17908..196a89653 100644 --- a/tracker/tracker/src/main/modules/mouse.ts +++ b/tracker/tracker/src/main/modules/mouse.ts @@ -1,4 +1,8 @@ -import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from "../utils.js"; +import { + normSpaces, + hasOpenreplayAttribute, + getLabelAttribute, +} from "../utils.js"; import App from "../app/index.js"; import { MouseMove, MouseClick } from "../../messages/index.js"; import { getInputLabel } from "./input.js"; @@ -24,6 +28,18 @@ function _getSelector(target: Element): string { return selector } +function isClickable(element: Element): boolean { + const tag = element.tagName.toUpperCase() + return tag === 'BUTTON' || + tag === 'A' || + tag === 'LI' || + tag === 'SELECT' || + (element as HTMLElement).onclick != null || + element.getAttribute('role') === 'button' + //|| element.className.includes("btn") + // MBTODO: intersect addEventListener +} + //TODO: fix (typescript doesn't allow work when the guard is inside the function) function getTarget(target: EventTarget | null): Element | null { if (target instanceof Element) { @@ -56,13 +72,7 @@ function _getTarget(target: Element): Element | null { if (tag === 'INPUT') { return element; } - if ( - tag === 'BUTTON' || - tag === 'A' || - tag === 'LI' || - tag === 'SELECT' || - (element as HTMLElement).onclick != null || - element.getAttribute('role') === 'button' || + if (isClickable(element) || getLabelAttribute(element) !== null ) { return element; @@ -83,19 +93,16 @@ export default function (app: App): void { if (dl !== null) { return dl; } - const tag = target.tagName.toUpperCase(); - if (tag === 'INPUT') { - return getInputLabel(target as HTMLInputElement) + if (target instanceof HTMLInputElement) { + return getInputLabel(target) } - if (tag === 'BUTTON' || - tag === 'A' || - tag === 'LI' || - tag === 'SELECT' || - (target as HTMLElement).onclick != null || - target.getAttribute('role') === 'button' - ) { - const label: string = app.sanitizer.getInnerTextSecure(target as HTMLElement); - return normSpaces(label).slice(0, 100); + if (isClickable(target)) { + let label = '' + if (target instanceof HTMLElement) { + label = app.sanitizer.getInnerTextSecure(target) + } + label = label || target.id || target.className + return normSpaces(label).slice(0, 100) } return ''; } @@ -126,7 +133,7 @@ export default function (app: App): void { } app.attachEventListener( - document.documentElement, + document.documentElement, 'mouseover', (e: MouseEvent): void => { const target = getTarget(e.target); diff --git a/tracker/tracker/src/main/modules/scroll.ts b/tracker/tracker/src/main/modules/scroll.ts index 0f54ba8f9..f9c80e6d9 100644 --- a/tracker/tracker/src/main/modules/scroll.ts +++ b/tracker/tracker/src/main/modules/scroll.ts @@ -20,7 +20,7 @@ export default function (app: App): void { ), ); - const sendSetNodeScroll = app.safe((s, node): void => { + const sendSetNodeScroll = app.safe((s: [number, number], node: Node): void => { const id = app.nodes.getID(node); if (id !== undefined) { app.send(new SetNodeScroll(id, s[0], s[1])); @@ -34,6 +34,12 @@ export default function (app: App): void { nodeScroll.clear(); }); + app.nodes.attachNodeCallback(node => { + if (node instanceof Element && node.scrollLeft + node.scrollTop > 0) { + nodeScroll.set(node, [node.scrollLeft, node.scrollTop]); + } + }) + app.attachEventListener(window, 'scroll', (e: Event): void => { const target = e.target; if (target === document) { @@ -41,9 +47,7 @@ export default function (app: App): void { return; } if (target instanceof Element) { - { - nodeScroll.set(target, [target.scrollLeft, target.scrollTop]); - } + nodeScroll.set(target, [target.scrollLeft, target.scrollTop]); } }); diff --git a/tracker/tracker/src/webworker/index.ts b/tracker/tracker/src/webworker/index.ts index cf0d1586a..f598ac0a4 100644 --- a/tracker/tracker/src/webworker/index.ts +++ b/tracker/tracker/src/webworker/index.ts @@ -31,42 +31,18 @@ let sendIntervalID: ReturnType | null = null; const sendQueue: Array = []; let busy = false; let attemptsCount = 0; -let ATTEMPT_TIMEOUT = 8000; +let ATTEMPT_TIMEOUT = 3000; let MAX_ATTEMPTS_COUNT = 10; // TODO?: exploit https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon function sendBatch(batch: Uint8Array):void { - const req = new XMLHttpRequest(); + const xhr = new XMLHttpRequest(); // TODO: async=false (3d param) instead of sendQueue array ? - req.open("POST", ingestPoint + "/v1/web/i", false); // TODO opaque request? - req.setRequestHeader("Authorization", "Bearer " + token); - // req.setRequestHeader("Content-Type", ""); - req.onreadystatechange = function() { - if (this.readyState === 4) { - if (this.status == 0) { - return; // happens simultaneously with onerror TODO: clear codeflow - } - if (this.status >= 400) { // TODO: test workflow. After 400+ it calls /start for some reason - busy = false; - reset(); - sendQueue.length = 0; - if (this.status === 401) { // Unauthorised (Token expired) - self.postMessage("restart") - return - } - self.postMessage(null); - return - } - //if (this.response == null) - const nextBatch = sendQueue.shift(); - if (nextBatch) { - sendBatch(nextBatch); - } else { - busy = false; - } - } - }; - req.onerror = function(e) { + xhr.open("POST", ingestPoint + "/v1/web/i", false); + xhr.setRequestHeader("Authorization", "Bearer " + token); + // xhr.setRequestHeader("Content-Type", ""); + + function retry() { if (attemptsCount >= MAX_ATTEMPTS_COUNT) { reset(); self.postMessage(null); @@ -75,8 +51,32 @@ function sendBatch(batch: Uint8Array):void { attemptsCount++; setTimeout(() => sendBatch(batch), ATTEMPT_TIMEOUT); } - // TODO: handle offline exception - req.send(batch.buffer); + xhr.onreadystatechange = function() { + if (this.readyState === 4) { + if (this.status == 0) { + return; // happens simultaneously with onerror TODO: clear codeflow + } + if (this.status === 401) { // Unauthorised (Token expired) + busy = false + self.postMessage("restart") + return + } else if (this.status >= 400) { // TODO: test workflow. After 400+ it calls /start for some reason + retry() + return + } + // Success + attemptsCount = 0 + const nextBatch = sendQueue.shift(); + if (nextBatch) { + sendBatch(nextBatch); + } else { + busy = false; + } + } + }; + xhr.onerror = retry // TODO: when in Offline mode it doesn't handle the error + // TODO: handle offline exception (?) + xhr.send(batch.buffer); } function send(): void { @@ -101,6 +101,7 @@ function reset() { clearInterval(sendIntervalID); sendIntervalID = null; } + sendQueue.length = 0; writer.reset(); } From 3fba26180d1a4f844166e59e1059d1f245bc0e2c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 11 Apr 2022 11:15:14 +0200 Subject: [PATCH 105/294] change(ui) - assist session data endpoint --- frontend/app/components/BugFinder/BugFinder.js | 4 ++-- frontend/app/duck/sessions.js | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/app/components/BugFinder/BugFinder.js b/frontend/app/components/BugFinder/BugFinder.js index 326a1e78e..85f899290 100644 --- a/frontend/app/components/BugFinder/BugFinder.js +++ b/frontend/app/components/BugFinder/BugFinder.js @@ -20,7 +20,7 @@ import { LAST_7_DAYS } from 'Types/app/period'; import { resetFunnel } from 'Duck/funnels'; import { resetFunnelFilters } from 'Duck/funnelFilters' import NoSessionsMessage from 'Shared/NoSessionsMessage'; -import TrackerUpdateMessage from 'Shared/TrackerUpdateMessage'; +// import TrackerUpdateMessage from 'Shared/TrackerUpdateMessage'; import SessionSearch from 'Shared/SessionSearch'; import MainSearchBar from 'Shared/MainSearchBar'; import { clearSearch, fetchSessions } from 'Duck/search'; @@ -130,7 +130,7 @@ export default class BugFinder extends React.PureComponent { />
- + {/* */}
diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js index f3df333c7..79822bb25 100644 --- a/frontend/app/duck/sessions.js +++ b/frontend/app/duck/sessions.js @@ -285,10 +285,11 @@ export function fetchErrorStackList(sessionId, errorId) { }; } -export const fetch = (sessionId) => (dispatch, getState) => { +export const fetch = (sessionId, isLive = false) => (dispatch, getState) => { + console.log('isLive', isLive) dispatch({ types: FETCH.toArray(), - call: client => client.get(`/sessions2/${ sessionId }`), + call: client => client.get(isLive ? `/assist/sessions/${ sessionId }` : `/sessions2/${ sessionId }`), filter: getState().getIn([ 'filters', 'appliedFilter' ]) }); } From 258c4da2a4132e2e21c40ee8c370ed54a218735e Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 11 Apr 2022 12:25:55 +0200 Subject: [PATCH 106/294] fix(ui) - localstorage boolean values --- frontend/app/player/Player.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/app/player/Player.ts b/frontend/app/player/Player.ts index 67875b530..f1f305868 100644 --- a/frontend/app/player/Player.ts +++ b/frontend/app/player/Player.ts @@ -30,10 +30,10 @@ const AUTOPLAY_STORAGE_KEY = "__$player-autoplay$__"; const SHOW_EVENTS_STORAGE_KEY = "__$player-show-events$__"; const storedSpeed: number = parseInt(localStorage.getItem(SPEED_STORAGE_KEY) || "") ; const initialSpeed = [1,2,4,8,16].includes(storedSpeed) ? storedSpeed : 1; -const initialSkip = !!localStorage.getItem(SKIP_STORAGE_KEY); -const initialSkipToIssue = !!localStorage.getItem(SKIP_TO_ISSUE_STORAGE_KEY); -const initialAutoplay = !!localStorage.getItem(AUTOPLAY_STORAGE_KEY); -const initialShowEvents = !!localStorage.getItem(SHOW_EVENTS_STORAGE_KEY); +const initialSkip = localStorage.getItem(SKIP_STORAGE_KEY) === 'true'; +const initialSkipToIssue = localStorage.getItem(SKIP_TO_ISSUE_STORAGE_KEY) === 'true'; +const initialAutoplay = localStorage.getItem(AUTOPLAY_STORAGE_KEY) === 'true'; +const initialShowEvents = localStorage.getItem(SHOW_EVENTS_STORAGE_KEY) === 'true'; export const INITIAL_STATE = { ...SUPER_INITIAL_STATE, From ef6b78b1c47dd35282ff02f85267694a8ef83f58 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 12:45:25 +0200 Subject: [PATCH 107/294] feat(api): enhanced auth feat(api): check for project existence before any response --- api/chalicelib/core/authorizers.py | 6 +++--- api/chalicelib/core/projects.py | 3 ++- api/chalicelib/core/users.py | 1 - ee/api/.gitignore | 1 + ee/api/chalicelib/core/projects.py | 3 ++- ee/api/chalicelib/core/users.py | 1 - ee/api/routers/base.py | 14 -------------- 7 files changed, 8 insertions(+), 21 deletions(-) delete mode 100644 ee/api/routers/base.py diff --git a/api/chalicelib/core/authorizers.py b/api/chalicelib/core/authorizers.py index 899fd046f..5756e82ab 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"front:default-foss"] + audience=[f"plugin:{helper.get_stage_name()}", f"front:{helper.get_stage_name()}"] ) except jwt.ExpiredSignatureError: print("! JWT Expired signature") @@ -42,7 +42,7 @@ def generate_jwt(id, tenant_id, iat, aud): payload={ "userId": id, "tenantId": tenant_id, - "exp": iat // 1000 + config("jwt_exp_delta_seconds",cast=int) + TimeUTC.get_utc_offset() // 1000, + "exp": iat // 1000 + config("jwt_exp_delta_seconds", cast=int) + TimeUTC.get_utc_offset() // 1000, "iss": config("jwt_issuer"), "iat": iat // 1000, "aud": aud diff --git a/api/chalicelib/core/projects.py b/api/chalicelib/core/projects.py index 3559f645a..e4ac36ad8 100644 --- a/api/chalicelib/core/projects.py +++ b/api/chalicelib/core/projects.py @@ -244,7 +244,8 @@ def get_project_key(project_id): where project_id =%(project_id)s AND deleted_at ISNULL;""", {"project_id": project_id}) ) - return cur.fetchone()["project_key"] + project = cur.fetchone() + return project["project_key"] if project is not None else None def get_capture_status(project_id): diff --git a/api/chalicelib/core/users.py b/api/chalicelib/core/users.py index b4ac0f869..ceada34f8 100644 --- a/api/chalicelib/core/users.py +++ b/api/chalicelib/core/users.py @@ -571,7 +571,6 @@ def auth_exists(user_id, tenant_id, jwt_iat, jwt_aud): ) -@dev.timed def authenticate(email, password, for_change_password=False, for_plugin=False): with pg_client.PostgresClient() as cur: query = cur.mogrify( diff --git a/ee/api/.gitignore b/ee/api/.gitignore index 488fab072..0c649b68e 100644 --- a/ee/api/.gitignore +++ b/ee/api/.gitignore @@ -242,6 +242,7 @@ Pipfile /auth/auth_apikey.py /auth/auth_jwt.py /build.sh +/routers/base.py /routers/core.py /routers/crons/core_crons.py /routers/subs/dashboard.py diff --git a/ee/api/chalicelib/core/projects.py b/ee/api/chalicelib/core/projects.py index 3072f55a0..0f2b62cc9 100644 --- a/ee/api/chalicelib/core/projects.py +++ b/ee/api/chalicelib/core/projects.py @@ -257,7 +257,8 @@ def get_project_key(project_id): where project_id =%(project_id)s AND deleted_at ISNULL;""", {"project_id": project_id}) ) - return cur.fetchone()["project_key"] + project = cur.fetchone() + return project["project_key"] if project is not None else None def get_capture_status(project_id): diff --git a/ee/api/chalicelib/core/users.py b/ee/api/chalicelib/core/users.py index 9ca77c1ea..b70f6a269 100644 --- a/ee/api/chalicelib/core/users.py +++ b/ee/api/chalicelib/core/users.py @@ -632,7 +632,6 @@ def change_jwt_iat(user_id): return cur.fetchone().get("jwt_iat") -@dev.timed def authenticate(email, password, for_change_password=False, for_plugin=False): with pg_client.PostgresClient() as cur: query = cur.mogrify( diff --git a/ee/api/routers/base.py b/ee/api/routers/base.py deleted file mode 100644 index 5c665b2d1..000000000 --- a/ee/api/routers/base.py +++ /dev/null @@ -1,14 +0,0 @@ -from fastapi import APIRouter, Depends - -from auth.auth_apikey import APIKeyAuth -from auth.auth_jwt import JWTAuth -from auth.auth_project import ProjectAuthorizer -from or_dependencies import ORRoute - - -def get_routers() -> (APIRouter, APIRouter, APIRouter): - public_app = APIRouter(route_class=ORRoute) - app = APIRouter(dependencies=[Depends(JWTAuth()), Depends(ProjectAuthorizer("projectId"))], route_class=ORRoute) - app_apikey = APIRouter(dependencies=[Depends(APIKeyAuth()), Depends(ProjectAuthorizer("projectKey"))], - route_class=ORRoute) - return public_app, app, app_apikey From d4dda5c8222639b037ccaf772665ac1fc8b3e5ff Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 12:46:28 +0200 Subject: [PATCH 108/294] feat(api): check for project existence before any response --- api/routers/base.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/routers/base.py b/api/routers/base.py index ff7fe165f..5c665b2d1 100644 --- a/api/routers/base.py +++ b/api/routers/base.py @@ -2,11 +2,13 @@ from fastapi import APIRouter, Depends from auth.auth_apikey import APIKeyAuth from auth.auth_jwt import JWTAuth +from auth.auth_project import ProjectAuthorizer from or_dependencies import ORRoute def get_routers() -> (APIRouter, APIRouter, APIRouter): public_app = APIRouter(route_class=ORRoute) - app = APIRouter(dependencies=[Depends(JWTAuth())], route_class=ORRoute) - app_apikey = APIRouter(dependencies=[Depends(APIKeyAuth())], route_class=ORRoute) + app = APIRouter(dependencies=[Depends(JWTAuth()), Depends(ProjectAuthorizer("projectId"))], route_class=ORRoute) + app_apikey = APIRouter(dependencies=[Depends(APIKeyAuth()), Depends(ProjectAuthorizer("projectKey"))], + route_class=ORRoute) return public_app, app, app_apikey From 5cd5ad656d22db1952a626719c835d784c78a19c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 12:46:45 +0200 Subject: [PATCH 109/294] feat(api): check for project existence before any response --- api/auth/auth_project.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 api/auth/auth_project.py diff --git a/api/auth/auth_project.py b/api/auth/auth_project.py new file mode 100644 index 000000000..98a495bbb --- /dev/null +++ b/api/auth/auth_project.py @@ -0,0 +1,24 @@ +from fastapi import Request +from starlette import status +from starlette.exceptions import HTTPException + +import schemas +from chalicelib.core import projects +from or_dependencies import OR_context + + +class ProjectAuthorizer: + def __init__(self, project_identifier): + self.project_identifier: str = project_identifier + + async def __call__(self, request: Request) -> None: + if len(request.path_params.keys()) == 0 or request.path_params.get(self.project_identifier) is None: + return + current_user: schemas.CurrentContext = await OR_context(request) + project_identifier = request.path_params[self.project_identifier] + if (self.project_identifier == "projectId" \ + and projects.get_project(project_id=project_identifier, tenant_id=current_user.tenant_id) is None) \ + or (self.project_identifier.lower() == "projectKey" \ + and projects.get_internal_project_id(project_key=project_identifier) is None): + print("project not found") + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="project not found.") From 6ce6f72cf5095acd71e169f8931ab996e4ee1913 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 11 Apr 2022 13:15:44 +0200 Subject: [PATCH 110/294] fix(ui) - funnel daterange --- .../DomBuildingTime/DomBuildingTime.tsx | 2 +- .../ResponseTime/ResponseTime.tsx | 2 +- .../components/WidgetChart/WidgetChart.tsx | 11 +++++------ .../components/WidgetWrapper/WidgetWrapper.tsx | 2 +- .../Funnels/FunnelHeader/FunnelHeader.js | 17 +++++++++-------- frontend/app/components/Session/LiveSession.js | 2 +- .../shared/LiveSessionList/LiveSessionList.tsx | 10 +++++----- .../LiveSessionSearch/LiveSessionSearch.tsx | 10 +++++----- frontend/app/duck/funnels.js | 18 +++++++++++++----- frontend/app/duck/sessions.js | 2 +- frontend/app/mstore/dashboardStore.ts | 2 +- frontend/app/mstore/types/dashboard.ts | 2 +- frontend/app/mstore/types/widget.ts | 1 + 13 files changed, 45 insertions(+), 36 deletions(-) diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx index 4bda65afa..c1e490422 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx @@ -47,7 +47,7 @@ function DomBuildingTime(props: Props) { />
- +
- + props.metric); const { dashboardStore } = useStore(); const period = useObserver(() => dashboardStore.period); const colors = Styles.customMetricColors; const [loading, setLoading] = useState(false) const [seriesMap, setSeriesMap] = useState([]); - const params = { density: 70 } - const metricParams = { ...params, metricId: metric.metricId, viewType: 'lineChart' } + const params = { density: 28 } + const metricParams = { ...params } const prevMetricRef = useRef(); const [data, setData] = useState(metric.data); @@ -34,7 +33,7 @@ function WidgetChart(props: Props) { prevMetricRef.current = metric; setLoading(true); - const data = isWidget ? {} : { ...metricParams, ...metric.toJson() }; + const data = isWidget ? { ...params } : { ...metricParams, ...metric.toJson() }; dashboardStore.fetchMetricChartData(metric, data, isWidget).then((res: any) => { setData(res); }).finally(() => { diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 9c723129e..b27175c1a 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -107,7 +107,7 @@ function WidgetWrapper(props: Props) { )}
- +
diff --git a/frontend/app/components/Funnels/FunnelHeader/FunnelHeader.js b/frontend/app/components/Funnels/FunnelHeader/FunnelHeader.js index 6130351e2..2297ca324 100644 --- a/frontend/app/components/Funnels/FunnelHeader/FunnelHeader.js +++ b/frontend/app/components/Funnels/FunnelHeader/FunnelHeader.js @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; import { Icon, BackLink, IconButton, Dropdown, Popup, TextEllipsis, Button } from 'UI'; import { remove as deleteFunnel, fetch, fetchInsights, fetchIssuesFiltered, fetchSessionsFiltered } from 'Duck/funnels'; -import { editFilter, refresh, addFilter } from 'Duck/funnels'; +import { editFilter, editFunnelFilter, refresh, addFilter } from 'Duck/funnels'; import DateRange from 'Shared/DateRange'; import { connect } from 'react-redux'; import { confirm } from 'UI/Confirmation'; @@ -18,7 +18,7 @@ const Info = ({ label = '', value = '', className = 'mx-4' }) => { } const FunnelHeader = (props) => { - const { funnel, insights, funnels, onBack, funnelId, showFilters = false, renameHandler } = props; + const { funnel, insights, funnels, onBack, funnelId, showFilters = false, funnelFilters, renameHandler } = props; const [showSaveModal, setShowSaveModal] = useState(false) const writeOption = (e, { name, value }) => { @@ -40,7 +40,7 @@ const FunnelHeader = (props) => { } const onDateChange = (e) => { - props.editFilter(e, funnelId); + props.editFunnelFilter(e, funnelId); } const options = funnels.map(({ funnelId, name }) => ({ text: name, value: funnelId })).toJS(); @@ -55,7 +55,7 @@ const FunnelHeader = (props) => { show={showSaveModal} closeHandler={() => setShowSaveModal(false)} /> -
+
{ />
@@ -109,5 +109,6 @@ const FunnelHeader = (props) => { } export default connect(state => ({ + funnelFilters: state.getIn([ 'funnels', 'funnelFilters' ]).toJS(), funnel: state.getIn([ 'funnels', 'instance' ]), -}), { editFilter, deleteFunnel, fetch, fetchInsights, fetchIssuesFiltered, fetchSessionsFiltered, refresh })(FunnelHeader) +}), { editFilter, editFunnelFilter, deleteFunnel, fetch, fetchInsights, fetchIssuesFiltered, fetchSessionsFiltered, refresh })(FunnelHeader) diff --git a/frontend/app/components/Session/LiveSession.js b/frontend/app/components/Session/LiveSession.js index 46bd39cf5..0833112a2 100644 --- a/frontend/app/components/Session/LiveSession.js +++ b/frontend/app/components/Session/LiveSession.js @@ -27,7 +27,7 @@ function LiveSession({ useEffect(() => { if (sessionId != null) { - fetchSession(sessionId) + fetchSession(sessionId, true) } else { console.error("No sessionID in route.") } diff --git a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx index a1617e47d..b0dbd80ac 100644 --- a/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx +++ b/frontend/app/components/shared/LiveSessionList/LiveSessionList.tsx @@ -45,11 +45,11 @@ function LiveSessionList(props: Props) { // const displayedCount = Math.min(currentPage * PER_PAGE, sessions.size); // const addPage = () => props.updateCurrentPage(props.currentPage + 1) - useEffect(() => { - if (filters.size === 0) { - props.addFilterByKeyAndValue(FilterKey.USERID, ''); - } - }, []); + // useEffect(() => { + // if (filters.size === 0) { + // props.addFilterByKeyAndValue(FilterKey.USERID, ''); + // } + // }, []); useEffect(() => { if (metaList.size === 0 || !!sort.field) return; diff --git a/frontend/app/components/shared/LiveSessionSearch/LiveSessionSearch.tsx b/frontend/app/components/shared/LiveSessionSearch/LiveSessionSearch.tsx index ae7a60b28..5f6e5f7cc 100644 --- a/frontend/app/components/shared/LiveSessionSearch/LiveSessionSearch.tsx +++ b/frontend/app/components/shared/LiveSessionSearch/LiveSessionSearch.tsx @@ -42,9 +42,9 @@ function LiveSessionSearch(props: Props) { }); props.edit({ filters: newFilters, }); - if (newFilters.size === 0) { - props.addFilterByKeyAndValue(FilterKey.USERID, ''); - } + // if (newFilters.size === 0) { + // props.addFilterByKeyAndValue(FilterKey.USERID, ''); + // } } const onChangeEventsOrder = (e, { name, value }) => { @@ -53,7 +53,7 @@ function LiveSessionSearch(props: Props) { }); } - return (hasEvents || hasFilters) ? ( + return (
- ) : <>; + ); } export default connect(state => ({ diff --git a/frontend/app/duck/funnels.js b/frontend/app/duck/funnels.js index 1278614be..3abfcc450 100644 --- a/frontend/app/duck/funnels.js +++ b/frontend/app/duck/funnels.js @@ -24,6 +24,7 @@ const SAVE = saveType('funnel/SAVE'); const UPDATE = saveType('funnel/UPDATE'); const EDIT = editType('funnel/EDIT'); const EDIT_FILTER = `${name}/EDIT_FILTER`; +const EDIT_FUNNEL_FILTER = `${name}/EDIT_FUNNEL_FILTER`; const REMOVE = removeType('funnel/REMOVE'); const INIT = initType('funnel/INIT'); const SET_NAV_REF = 'funnels/SET_NAV_REF' @@ -51,7 +52,6 @@ const REMOVE_SUCCESS = success(REMOVE); const range = getDateRangeFromValue(LAST_7_DAYS); const defaultDateFilters = { - events: [], rangeValue: LAST_7_DAYS, startDate: range.start.unix() * 1000, endDate: range.end.unix() * 1000 @@ -67,7 +67,7 @@ const initialState = Map({ sessionsTotal: 0, sessions: List(), activeStages: List(), - funnelFilters: defaultDateFilters, + funnelFilters: Map(defaultDateFilters), sessionsSort: Map({ order: "desc", sort: "newest" }), issueFilters: Map({ filters: List(), @@ -87,6 +87,8 @@ const reducer = (state = initialState, action = {}) => { return state.mergeIn([ 'instance' ], action.instance); case EDIT_FILTER: return state.mergeIn([ 'instance', 'filter' ], action.instance); + case EDIT_FUNNEL_FILTER: + return state.mergeIn([ 'funnelFilters' ], action.instance); case INIT: return state.set('instance', Funnel(action.instance)) case FETCH_LIST_SUCCESS: @@ -171,7 +173,7 @@ const reducer = (state = initialState, action = {}) => { .set('instance', Funnel()) .set('activeStages', List()) .set('issuesSort', Map({})) - .set('funnelFilters', defaultDateFilters) + // .set('funnelFilters', Map(defaultDateFilters)) .set('insights', Funnel()) .set('issues', List()) .set('sessions', List()); @@ -200,6 +202,7 @@ export const fetch = (funnelId, params) => (dispatch, getState) => { function getParams(params, state) { const filter = state.getIn([ 'funnels', 'instance', 'filter']).toData(); filter.filters = filter.filters.map(filterMap); + const funnelFilters = state.getIn([ 'funnels', 'funnelFilters']).toJS(); // const appliedFilter = state.getIn([ 'funnels', 'instance', 'filter' ]); // const filter = appliedFilter @@ -209,7 +212,7 @@ function getParams(params, state) { // filter.filters = state.getIn([ 'funnelFilters', 'appliedFilter', 'filters' ]) // .map(filterMap).toJS(); - return filter; + return { ...filter, ...funnelFilters }; } export const fetchInsights = (funnelId, params = {}, isRefresh = false) => (dispatch, getState) => { @@ -372,7 +375,7 @@ export const resetIssue = () => { export const resetFunnel = () => { return { - type: RESET_FUNNEL, + type: RESET_FUNNEL, } } @@ -428,6 +431,11 @@ export const editFilter = reduceThenFetchList((instance) => ({ instance, })); +export const editFunnelFilter = reduceThenFetchList((instance) => ({ + type: EDIT_FUNNEL_FILTER, + instance, +})); + export const addFilter = (filter) => (dispatch, getState) => { filter.value = checkFilterValue(filter.value); const instance = getState().getIn([ 'funnels', 'instance', 'filter']); diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js index 79822bb25..0c46cc54a 100644 --- a/frontend/app/duck/sessions.js +++ b/frontend/app/duck/sessions.js @@ -319,7 +319,7 @@ export function fetchInsights(params) { export function fetchLiveList() { return { types: FETCH_LIVE_LIST.toArray(), - call: client => client.get('/assist/sessions'), + call: client => client.get('/assist/sessions', { userId: 'test'}), }; } diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 0231bbf7c..6739d54b6 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -169,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)).sort((a, b) => a.position - b.position) + this.dashboards = list.map(d => new Dashboard().fromJson(d)) }) }).finally(() => { runInAction(() => { diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts index e3db0a146..25cfa7a05 100644 --- a/frontend/app/mstore/types/dashboard.ts +++ b/frontend/app/mstore/types/dashboard.ts @@ -96,7 +96,7 @@ export default class Dashboard implements IDashboard { this.isPublic = json.isPublic this.isPinned = json.isPinned // this.config = json.config - this.widgets = json.widgets ? json.widgets.map(w => new Widget().fromJson(w)) : [] + this.widgets = json.widgets ? json.widgets.map(w => new Widget().fromJson(w)).sort((a, b) => a.position - b.position) : [] }) return this } diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index ee9c559b4..c32b78ed2 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -130,6 +130,7 @@ export default class Widget implements IWidget { this.position = json.config.position this.predefinedKey = json.predefinedKey }) + console.log(this.name, this.position) return this } From 410ffb14754cc40b8a49e04cccb51901724c2480 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Mon, 11 Apr 2022 13:57:09 +0200 Subject: [PATCH 111/294] change(ui) - assist filter by userid --- .../Assist/components/SessionList/SessionList.tsx | 8 ++++++-- frontend/app/duck/sessions.js | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/app/components/Assist/components/SessionList/SessionList.tsx b/frontend/app/components/Assist/components/SessionList/SessionList.tsx index 73c7a3a7f..32c267588 100644 --- a/frontend/app/components/Assist/components/SessionList/SessionList.tsx +++ b/frontend/app/components/Assist/components/SessionList/SessionList.tsx @@ -8,11 +8,15 @@ interface Props { loading: boolean, list: any, session: any, - fetchLiveList: () => void, + fetchLiveList: (params: any) => void, } function SessionList(props: Props) { useEffect(() => { - props.fetchLiveList(); + const params: any = {} + if (props.session.userId) { + params.userId = props.session.userId + } + props.fetchLiveList(params); }, []) return ( diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js index 0c46cc54a..6fcdb15c5 100644 --- a/frontend/app/duck/sessions.js +++ b/frontend/app/duck/sessions.js @@ -316,10 +316,10 @@ export function fetchInsights(params) { }; } -export function fetchLiveList() { +export function fetchLiveList(params = {}) { return { types: FETCH_LIVE_LIST.toArray(), - call: client => client.get('/assist/sessions', { userId: 'test'}), + call: client => client.get('/assist/sessions', params), }; } From 888433d3c73ae1ae803865314a7ccb0bf5a6fcc4 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 14:39:27 +0200 Subject: [PATCH 112/294] feat(utilities): heapsnapshot --- utilities/server.js | 18 ++++++++++----- utilities/utils/dump.js | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 utilities/utils/dump.js diff --git a/utilities/server.js b/utilities/server.js index 661ef081c..225db88f4 100644 --- a/utilities/server.js +++ b/utilities/server.js @@ -1,14 +1,15 @@ -var sourcemapsReaderServer = require('./servers/sourcemaps-server'); -var {peerRouter, peerConnection, peerDisconnect, peerError} = require('./servers/peerjs-server'); -var express = require('express'); +const dumps = require('./utils/dump'); +const sourcemapsReaderServer = require('./servers/sourcemaps-server'); +const {peerRouter, peerConnection, peerDisconnect, peerError} = require('./servers/peerjs-server'); +const express = require('express'); const {ExpressPeerServer} = require('peer'); const socket = require("./servers/websocket"); const HOST = '0.0.0.0'; const PORT = 9000; -var app = express(); -var wsapp = express(); +const app = express(); +const wsapp = express(); let debug = process.env.debug === "1" || false; const request_logger = (identity) => { return (req, res, next) => { @@ -29,6 +30,13 @@ app.use('/sourcemaps', sourcemapsReaderServer); app.use('/assist', peerRouter); wsapp.use('/assist', socket.wsRouter); +app.get('/heapdump', dumps.sendHeapSnapshot); +app.get('/heapdump/save', dumps.saveHeapSnapshot); +wsapp.get('/heapdump', dumps.sendHeapSnapshot); +wsapp.get('/heapdump/save', dumps.saveHeapSnapshot); + +console.log(`Heapdump enabled. Send a request to "/heapdump" to download a heapdump,\nor "/heapdump/save" to only generate a heapdump.`); + const server = app.listen(PORT, HOST, () => { console.log(`App listening on http://${HOST}:${PORT}`); console.log('Press Ctrl+C to quit.'); diff --git a/utilities/utils/dump.js b/utilities/utils/dump.js new file mode 100644 index 000000000..c22cffc3d --- /dev/null +++ b/utilities/utils/dump.js @@ -0,0 +1,50 @@ +const fs = require('fs'); +const v8 = require('v8'); + +const location = '/tmp/'; + +async function createHeapSnapshot() { + const fileName = `${Date.now()}.heapsnapshot`; + await fs.promises.writeFile( + location + fileName, + v8.getHeapSnapshot() + ); + return fileName; +} + + +async function sendHeapSnapshot(req, res) { + const fileName = await createHeapSnapshot(); + const file = fs.readFileSync(location + fileName, 'binary'); + const stat = fs.statSync(location + fileName); + + res.setHeader('Content-Length', stat.size); + res.setHeader('Content-Type', 'audio/mpeg'); + res.setHeader('Content-Disposition', 'attachment; filename=' + fileName); + res.write(file, 'binary'); + res.end(); + try { + fs.unlinkSync(location + fileName) + } catch (err) { + console.error("error while deleting heapsnapshot file"); + console.error(err); + } +} + +process.on('USR2', () => { + console.info('USR2 signal received.'); +}); + +async function saveHeapSnapshot(req, res) { + const fileName = await createHeapSnapshot(); + const path = location + fileName; + console.log(`Heapdump saved to ${path}`) + res.statusCode = 200; + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({path})); +} + +process.on('USR2', () => { + console.info('USR2 signal received.'); +}); +module.exports = {sendHeapSnapshot, saveHeapSnapshot} \ No newline at end of file From 8786a121f6e678662cc9a469ca9751c84b09c5fc Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 14:45:54 +0200 Subject: [PATCH 113/294] feat(utilities): FOSS&EE heapsnapshot --- ee/utilities/.gitignore | 3 ++- ee/utilities/server.js | 16 ++++++++++------ utilities/server.js | 5 +---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ee/utilities/.gitignore b/ee/utilities/.gitignore index fc05191e0..2dd72c808 100644 --- a/ee/utilities/.gitignore +++ b/ee/utilities/.gitignore @@ -10,4 +10,5 @@ build.sh servers/peerjs-server.js servers/sourcemaps-handler.js servers/sourcemaps-server.js -#servers/websocket.js \ No newline at end of file +#servers/websocket.js +/utils/dump.js diff --git a/ee/utilities/server.js b/ee/utilities/server.js index d049faa19..8f5a34d7b 100644 --- a/ee/utilities/server.js +++ b/ee/utilities/server.js @@ -1,8 +1,9 @@ -var sourcemapsReaderServer = require('./servers/sourcemaps-server'); -var {peerRouter, peerConnection, peerDisconnect, peerError} = require('./servers/peerjs-server'); -var express = require('express'); +const dumps = require('./utils/dump'); +const sourcemapsReaderServer = require('./servers/sourcemaps-server'); +const {peerRouter, peerConnection, peerDisconnect, peerError} = require('./servers/peerjs-server'); +const express = require('express'); const {ExpressPeerServer} = require('peer'); -var socket; +let socket; if (process.env.redis === "true") { console.log("Using Redis"); socket = require("./servers/websocket-cluster"); @@ -13,7 +14,7 @@ if (process.env.redis === "true") { const HOST = '0.0.0.0'; const PORT = 9000; -var app = express(); +const app = express(); let debug = process.env.debug === "1" || false; const request_logger = (identity) => { @@ -50,6 +51,8 @@ peerServer.on('disconnect', peerDisconnect); peerServer.on('error', peerError); app.use('/', peerServer); app.enable('trust proxy'); +app.get('/heapdump', dumps.sendHeapSnapshot); +app.get('/heapdump/save', dumps.saveHeapSnapshot); if (process.env.uws !== "true") { var wsapp = express(); @@ -125,4 +128,5 @@ if (process.env.uws !== "true") { // process.exit(1); }); module.exports = {uapp, server}; -} \ No newline at end of file +} +console.log(`Heapdump enabled. Send a request to "/heapdump" to download a heapdump,\nor "/heapdump/save" to only generate a heapdump.`); \ No newline at end of file diff --git a/utilities/server.js b/utilities/server.js index 225db88f4..fcbdb5c62 100644 --- a/utilities/server.js +++ b/utilities/server.js @@ -32,10 +32,6 @@ wsapp.use('/assist', socket.wsRouter); app.get('/heapdump', dumps.sendHeapSnapshot); app.get('/heapdump/save', dumps.saveHeapSnapshot); -wsapp.get('/heapdump', dumps.sendHeapSnapshot); -wsapp.get('/heapdump/save', dumps.saveHeapSnapshot); - -console.log(`Heapdump enabled. Send a request to "/heapdump" to download a heapdump,\nor "/heapdump/save" to only generate a heapdump.`); const server = app.listen(PORT, HOST, () => { console.log(`App listening on http://${HOST}:${PORT}`); @@ -59,3 +55,4 @@ app.enable('trust proxy'); wsapp.enable('trust proxy'); socket.start(wsserver); module.exports = {wsserver, server}; +console.log(`Heapdump enabled. Send a request to "/heapdump" to download a heapdump,\nor "/heapdump/save" to only generate a heapdump.`); \ No newline at end of file From 0deb16529010d128fbec81d8250c76738d3234a3 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 15:45:23 +0200 Subject: [PATCH 114/294] feat(utilities): heapsnapshot use express helper for upload --- utilities/utils/dump.js | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/utilities/utils/dump.js b/utilities/utils/dump.js index c22cffc3d..3c6f2de73 100644 --- a/utilities/utils/dump.js +++ b/utilities/utils/dump.js @@ -15,20 +15,14 @@ async function createHeapSnapshot() { async function sendHeapSnapshot(req, res) { const fileName = await createHeapSnapshot(); - const file = fs.readFileSync(location + fileName, 'binary'); - const stat = fs.statSync(location + fileName); - - res.setHeader('Content-Length', stat.size); - res.setHeader('Content-Type', 'audio/mpeg'); - res.setHeader('Content-Disposition', 'attachment; filename=' + fileName); - res.write(file, 'binary'); - res.end(); - try { - fs.unlinkSync(location + fileName) - } catch (err) { - console.error("error while deleting heapsnapshot file"); - console.error(err); - } + res.download(location + fileName, function (err) { + try { + fs.unlinkSync(location + fileName) + } catch (err) { + console.error("error while deleting heapsnapshot file"); + console.error(err); + } + }); } process.on('USR2', () => { From 29f264dcbf85c3c92908ebeb493c1f9b21e04b79 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 16:28:26 +0200 Subject: [PATCH 115/294] feat(utilities): heapsnapshot multiple steps --- ee/utilities/server.js | 3 +-- utilities/server.js | 3 +-- utilities/utils/dump.js | 53 ++++++++++++++++++++++++++++------------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/ee/utilities/server.js b/ee/utilities/server.js index 8f5a34d7b..ac39f5dd1 100644 --- a/ee/utilities/server.js +++ b/ee/utilities/server.js @@ -51,8 +51,7 @@ peerServer.on('disconnect', peerDisconnect); peerServer.on('error', peerError); app.use('/', peerServer); app.enable('trust proxy'); -app.get('/heapdump', dumps.sendHeapSnapshot); -app.get('/heapdump/save', dumps.saveHeapSnapshot); +app.use('/heapdump', dumps.router); if (process.env.uws !== "true") { var wsapp = express(); diff --git a/utilities/server.js b/utilities/server.js index fcbdb5c62..0c80170af 100644 --- a/utilities/server.js +++ b/utilities/server.js @@ -30,8 +30,7 @@ app.use('/sourcemaps', sourcemapsReaderServer); app.use('/assist', peerRouter); wsapp.use('/assist', socket.wsRouter); -app.get('/heapdump', dumps.sendHeapSnapshot); -app.get('/heapdump/save', dumps.saveHeapSnapshot); +app.use('/heapdump', dumps.router); const server = app.listen(PORT, HOST, () => { console.log(`App listening on http://${HOST}:${PORT}`); diff --git a/utilities/utils/dump.js b/utilities/utils/dump.js index 3c6f2de73..23708f07c 100644 --- a/utilities/utils/dump.js +++ b/utilities/utils/dump.js @@ -1,20 +1,35 @@ const fs = require('fs'); const v8 = require('v8'); +const express = require('express'); +const router = express.Router(); const location = '/tmp/'; +let creationStatus = null; +let fileName = null; async function createHeapSnapshot() { - const fileName = `${Date.now()}.heapsnapshot`; + if (creationStatus) { + return console.log(`In progress ${fileName}`); + } + if (fileName === null) { + fileName = `${Date.now()}.heapsnapshot`; + } + console.log(`Creating ${fileName}`); await fs.promises.writeFile( location + fileName, v8.getHeapSnapshot() ); - return fileName; + console.log(`Created ${fileName}`); + creationStatus = true; } -async function sendHeapSnapshot(req, res) { - const fileName = await createHeapSnapshot(); +async function downloadHeapSnapshot(req, res) { + if (creationStatus === null) { + return res.end("should call /new first"); + } else if (!creationStatus) { + return res.end("should wait for done status"); + } res.download(location + fileName, function (err) { try { fs.unlinkSync(location + fileName) @@ -25,20 +40,24 @@ async function sendHeapSnapshot(req, res) { }); } -process.on('USR2', () => { - console.info('USR2 signal received.'); -}); - -async function saveHeapSnapshot(req, res) { - const fileName = await createHeapSnapshot(); - const path = location + fileName; - console.log(`Heapdump saved to ${path}`) +function getHeapSnapshotStatus(req, res) { res.statusCode = 200; res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify({path})); + res.end(JSON.stringify({path: location + fileName, 'done': creationStatus})); } -process.on('USR2', () => { - console.info('USR2 signal received.'); -}); -module.exports = {sendHeapSnapshot, saveHeapSnapshot} \ No newline at end of file +function createNewHeapSnapshot(req, res) { + creationStatus = false; + fileName = `${Date.now()}.heapsnapshot`; + setTimeout(() => { + createHeapSnapshot() + }, 0); + res.statusCode = 200; + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({path: location + fileName, 'done': creationStatus})); +} + +router.get('/status', getHeapSnapshotStatus); +router.get(`/new`, createNewHeapSnapshot); +router.get(`/download`, downloadHeapSnapshot); +module.exports = {router} \ No newline at end of file From f2b429a3d898a6fb9444c972882f458e5aacc9be Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 16:36:16 +0200 Subject: [PATCH 116/294] feat(utilities): changed base docker image --- utilities/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/Dockerfile b/utilities/Dockerfile index 9b82358b3..171995974 100644 --- a/utilities/Dockerfile +++ b/utilities/Dockerfile @@ -1,4 +1,4 @@ -FROM node:17-stretch +FROM node:17-alpine WORKDIR /work COPY . . RUN npm install From 58af31c35e8b5a890b4381fd4b224922a9312e1d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 11 Apr 2022 18:17:49 +0200 Subject: [PATCH 117/294] feat(utilities): optimized websocket geo-ip logic feat(utilities): refactored websocket --- ee/utilities/server.js | 2 +- ee/utilities/servers/websocket-cluster.js | 35 +++++++++----------- ee/utilities/servers/websocket.js | 35 +++++++++----------- utilities/Dockerfile | 2 +- utilities/server.js | 2 +- utilities/servers/websocket.js | 35 +++++++++----------- utilities/utils/{dump.js => HeapSnapshot.js} | 0 7 files changed, 48 insertions(+), 63 deletions(-) rename utilities/utils/{dump.js => HeapSnapshot.js} (100%) diff --git a/ee/utilities/server.js b/ee/utilities/server.js index ac39f5dd1..798f4e715 100644 --- a/ee/utilities/server.js +++ b/ee/utilities/server.js @@ -1,4 +1,4 @@ -const dumps = require('./utils/dump'); +const dumps = require('./utils/HeapSnapshot'); const sourcemapsReaderServer = require('./servers/sourcemaps-server'); const {peerRouter, peerConnection, peerDisconnect, peerError} = require('./servers/peerjs-server'); const express = require('express'); diff --git a/ee/utilities/servers/websocket-cluster.js b/ee/utilities/servers/websocket-cluster.js index 998a457df..8dc3bf94c 100644 --- a/ee/utilities/servers/websocket-cluster.js +++ b/ee/utilities/servers/websocket-cluster.js @@ -255,6 +255,16 @@ async function get_all_agents_ids(io, socket) { return agents; } +let geoip = null; +geoip2Reader.open(process.env.MAXMINDDB_FILE, {}) + .then(reader => { + geoip = reader; + }) + .catch(error => { + console.log("Error while opening the MAXMINDDB_FILE.") + console.error(error); + }); + function extractSessionInfo(socket) { if (socket.handshake.query.sessionInfo !== undefined) { debug && console.log("received headers"); @@ -268,21 +278,11 @@ function extractSessionInfo(socket) { socket.handshake.query.sessionInfo.userDevice = ua.device.model || null; socket.handshake.query.sessionInfo.userDeviceType = ua.device.type || 'desktop'; socket.handshake.query.sessionInfo.userCountry = null; - - const options = { - // you can use options like `cache` or `watchForUpdates` - }; - // console.log("Looking for MMDB file in " + process.env.MAXMINDDB_FILE); - geoip2Reader.open(process.env.MAXMINDDB_FILE, options) - .then(reader => { - debug && console.log("looking for location of "); - debug && console.log(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); - let country = reader.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); - socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; - }) - .catch(error => { - console.error(error); - }); + if (geoip !== null) { + debug && console.log(`looking for location of ${socket.handshake.headers['x-forwarded-for'] || socket.handshake.address}`); + let country = geoip.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); + socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; + } } } @@ -294,10 +294,6 @@ module.exports = { debug && console.log(`WS started:${socket.id}, Query:${JSON.stringify(socket.handshake.query)}`); socket.peerId = socket.handshake.query.peerId; socket.identity = socket.handshake.query.identity; - const {projectKey, sessionId} = extractPeerId(socket.peerId); - socket.sessionId = sessionId; - socket.projectKey = projectKey; - socket.lastMessageReceivedAt = Date.now(); let {c_sessions, c_agents} = await sessions_agents_count(io, socket); if (socket.identity === IDENTITIES.session) { if (c_sessions > 0) { @@ -361,7 +357,6 @@ module.exports = { }); socket.onAny(async (eventName, ...args) => { - socket.lastMessageReceivedAt = Date.now(); if (socket.identity === IDENTITIES.session) { debug && console.log(`received event:${eventName}, from:${socket.identity}, sending message to room:${socket.peerId}`); socket.to(socket.peerId).emit(eventName, args[0]); diff --git a/ee/utilities/servers/websocket.js b/ee/utilities/servers/websocket.js index 256286351..521655b34 100644 --- a/ee/utilities/servers/websocket.js +++ b/ee/utilities/servers/websocket.js @@ -233,6 +233,16 @@ async function get_all_agents_ids(io, socket) { return agents; } +let geoip = null; +geoip2Reader.open(process.env.MAXMINDDB_FILE, {}) + .then(reader => { + geoip = reader; + }) + .catch(error => { + console.log("Error while opening the MAXMINDDB_FILE.") + console.error(error); + }); + function extractSessionInfo(socket) { if (socket.handshake.query.sessionInfo !== undefined) { debug && console.log("received headers"); @@ -246,21 +256,11 @@ function extractSessionInfo(socket) { socket.handshake.query.sessionInfo.userDevice = ua.device.model || null; socket.handshake.query.sessionInfo.userDeviceType = ua.device.type || 'desktop'; socket.handshake.query.sessionInfo.userCountry = null; - - const options = { - // you can use options like `cache` or `watchForUpdates` - }; - // console.log("Looking for MMDB file in " + process.env.MAXMINDDB_FILE); - geoip2Reader.open(process.env.MAXMINDDB_FILE, options) - .then(reader => { - debug && console.log("looking for location of "); - debug && console.log(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); - let country = reader.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); - socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; - }) - .catch(error => { - console.error(error); - }); + if (geoip !== null) { + debug && console.log(`looking for location of ${socket.handshake.headers['x-forwarded-for'] || socket.handshake.address}`); + let country = geoip.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); + socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; + } } } @@ -272,10 +272,6 @@ module.exports = { debug && console.log(`WS started:${socket.id}, Query:${JSON.stringify(socket.handshake.query)}`); socket.peerId = socket.handshake.query.peerId; socket.identity = socket.handshake.query.identity; - const {projectKey, sessionId} = extractPeerId(socket.peerId); - socket.sessionId = sessionId; - socket.projectKey = projectKey; - socket.lastMessageReceivedAt = Date.now(); let {c_sessions, c_agents} = await sessions_agents_count(io, socket); if (socket.identity === IDENTITIES.session) { if (c_sessions > 0) { @@ -337,7 +333,6 @@ module.exports = { }); socket.onAny(async (eventName, ...args) => { - socket.lastMessageReceivedAt = Date.now(); if (socket.identity === IDENTITIES.session) { debug && console.log(`received event:${eventName}, from:${socket.identity}, sending message to room:${socket.peerId}`); socket.to(socket.peerId).emit(eventName, args[0]); diff --git a/utilities/Dockerfile b/utilities/Dockerfile index 171995974..9b82358b3 100644 --- a/utilities/Dockerfile +++ b/utilities/Dockerfile @@ -1,4 +1,4 @@ -FROM node:17-alpine +FROM node:17-stretch WORKDIR /work COPY . . RUN npm install diff --git a/utilities/server.js b/utilities/server.js index 0c80170af..5b32f5ec7 100644 --- a/utilities/server.js +++ b/utilities/server.js @@ -1,4 +1,4 @@ -const dumps = require('./utils/dump'); +const dumps = require('./utils/HeapSnapshot'); const sourcemapsReaderServer = require('./servers/sourcemaps-server'); const {peerRouter, peerConnection, peerDisconnect, peerError} = require('./servers/peerjs-server'); const express = require('express'); diff --git a/utilities/servers/websocket.js b/utilities/servers/websocket.js index 7371f206c..5f7000322 100644 --- a/utilities/servers/websocket.js +++ b/utilities/servers/websocket.js @@ -204,6 +204,16 @@ async function get_all_agents_ids(io, socket) { return agents; } +let geoip = null; +geoip2Reader.open(process.env.MAXMINDDB_FILE, {}) + .then(reader => { + geoip = reader; + }) + .catch(error => { + console.log("Error while opening the MAXMINDDB_FILE.") + console.error(error); + }); + function extractSessionInfo(socket) { if (socket.handshake.query.sessionInfo !== undefined) { debug && console.log("received headers"); @@ -217,21 +227,11 @@ function extractSessionInfo(socket) { socket.handshake.query.sessionInfo.userDevice = ua.device.model || null; socket.handshake.query.sessionInfo.userDeviceType = ua.device.type || 'desktop'; socket.handshake.query.sessionInfo.userCountry = null; - - const options = { - // you can use options like `cache` or `watchForUpdates` - }; - // console.log("Looking for MMDB file in " + process.env.MAXMINDDB_FILE); - geoip2Reader.open(process.env.MAXMINDDB_FILE, options) - .then(reader => { - debug && console.log("looking for location of "); - debug && console.log(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); - let country = reader.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); - socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; - }) - .catch(error => { - console.error(error); - }); + if (geoip !== null) { + debug && console.log(`looking for location of ${socket.handshake.headers['x-forwarded-for'] || socket.handshake.address}`); + let country = geoip.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); + socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; + } } } @@ -243,10 +243,6 @@ module.exports = { debug && console.log(`WS started:${socket.id}, Query:${JSON.stringify(socket.handshake.query)}`); socket.peerId = socket.handshake.query.peerId; socket.identity = socket.handshake.query.identity; - const {projectKey, sessionId} = extractPeerId(socket.peerId); - socket.sessionId = sessionId; - socket.projectKey = projectKey; - socket.lastMessageReceivedAt = Date.now(); let {c_sessions, c_agents} = await sessions_agents_count(io, socket); if (socket.identity === IDENTITIES.session) { if (c_sessions > 0) { @@ -308,7 +304,6 @@ module.exports = { }); socket.onAny(async (eventName, ...args) => { - socket.lastMessageReceivedAt = Date.now(); if (socket.identity === IDENTITIES.session) { debug && console.log(`received event:${eventName}, from:${socket.identity}, sending message to room:${socket.peerId}`); socket.to(socket.peerId).emit(eventName, args[0]); diff --git a/utilities/utils/dump.js b/utilities/utils/HeapSnapshot.js similarity index 100% rename from utilities/utils/dump.js rename to utilities/utils/HeapSnapshot.js From fa7ae29a6284ee31be3db0a223296c958e73147c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 12 Apr 2022 12:38:23 +0200 Subject: [PATCH 118/294] change(ui) - player timeline slider and other fixes --- .../BugFinder/SessionList/SessionList.js | 73 ++++++-------- .../CustomMetricPercentage.tsx | 2 +- .../components/Session/Layout/PlayOverlay.js | 21 ++-- .../Session/Layout/Player/Timeline.js | 5 +- .../Session_/Player/Controls/Circle.tsx | 18 ++++ .../Session_/Player/Controls/Controls.js | 11 ++- .../Player/Controls/CustomDragLayer.tsx | 98 +++++++++++++++++++ .../Player/Controls/DraggableCircle.tsx | 67 +++++++++++++ .../Session_/Player/Controls/TimeTracker.js | 4 - .../Session_/Player/Controls/Timeline.js | 41 +++++++- .../Session_/Player/Controls/time.css | 2 + .../Session_/Player/Controls/timeline.css | 25 +++++ .../Session_/Player/Overlay/PlayIconLayer.tsx | 24 ++++- .../app/components/Session_/Player/Player.js | 2 + .../ui/TimelinePointer/timelinePointer.css | 2 +- frontend/app/duck/search.js | 11 +++ frontend/app/initialize.js | 7 -- frontend/app/svg/icons/pie-chart-fill.svg | 2 +- 18 files changed, 327 insertions(+), 88 deletions(-) create mode 100644 frontend/app/components/Session_/Player/Controls/Circle.tsx create mode 100644 frontend/app/components/Session_/Player/Controls/CustomDragLayer.tsx create mode 100644 frontend/app/components/Session_/Player/Controls/DraggableCircle.tsx diff --git a/frontend/app/components/BugFinder/SessionList/SessionList.js b/frontend/app/components/BugFinder/SessionList/SessionList.js index 858e9cb30..27324e686 100644 --- a/frontend/app/components/BugFinder/SessionList/SessionList.js +++ b/frontend/app/components/BugFinder/SessionList/SessionList.js @@ -1,12 +1,12 @@ import { connect } from 'react-redux'; -import { Loader, NoContent, Button, LoadMoreButton, Pagination } from 'UI'; +import { Loader, NoContent, Button, Pagination } from 'UI'; import { applyFilter, addAttribute, addEvent } from 'Duck/filters'; -import { fetchSessions, addFilterByKeyAndValue, updateCurrentPage } from 'Duck/search'; +import { fetchSessions, addFilterByKeyAndValue, updateCurrentPage, setScrollPosition } from 'Duck/search'; import SessionItem from 'Shared/SessionItem'; import SessionListHeader from './SessionListHeader'; import { FilterKey } from 'Types/filter/filterType'; -const ALL = 'all'; +// const ALL = 'all'; const PER_PAGE = 10; const AUTOREFRESH_INTERVAL = 3 * 60 * 1000; var timeoutId; @@ -21,6 +21,7 @@ var timeoutId; filters: state.getIn([ 'search', 'instance', 'filters' ]), metaList: state.getIn(['customFields', 'list']).map(i => i.key), currentPage: state.getIn([ 'search', 'currentPage' ]), + scrollY: state.getIn([ 'search', 'scrollY' ]), }), { applyFilter, addAttribute, @@ -28,24 +29,15 @@ var timeoutId; fetchSessions, addFilterByKeyAndValue, updateCurrentPage, + setScrollPosition, }) export default class SessionList extends React.PureComponent { - state = { - showPages: 1, - } + constructor(props) { super(props); this.timeout(); } - componentDidUpdate(prevProps) { - if (prevProps.loading && !this.props.loading) { - this.setState({ showPages: 1 }); - } - } - - addPage = () => this.setState({ showPages: this.state.showPages + 1 }) - onUserClick = (userId, userAnonymousId) => { if (userId) { this.props.addFilterByKeyAndValue(FilterKey.USERID, userId); @@ -75,17 +67,22 @@ export default class SessionList extends React.PureComponent { } componentWillUnmount() { + this.props.setScrollPosition(window.scrollY) clearTimeout(timeoutId) } - + componentDidMount() { + const { scrollY } = this.props; + console.log('scrollY', scrollY); + window.scrollTo(0, scrollY); + } renderActiveTabContent(list) { const { loading, filters, - onMenuItemClick, - allList, + // onMenuItemClick, + // allList, activeTab, metaList, currentPage, @@ -93,8 +90,6 @@ export default class SessionList extends React.PureComponent { } = this.props; const _filterKeys = filters.map(i => i.key); const hasUserFilter = _filterKeys.includes(FilterKey.USERID) || _filterKeys.includes(FilterKey.USERANONYMOUSID); - const { showPages } = this.state; - const displayedCount = Math.min(showPages * PER_PAGE, list.size); return (
Please try changing your search parameters.
- {allList.size > 0 && ( + {/* {allList.size > 0 && (
However, we found other sessions based on your search parameters.
@@ -115,7 +110,7 @@ export default class SessionList extends React.PureComponent { >See All
- )} + )} */}
} > @@ -139,41 +134,29 @@ export default class SessionList extends React.PureComponent { debounceRequest={1000} />
- {/* - Haven't found the session in the above list?
Try being a bit more specific by setting a specific time frame or simply use different filters -
- } - /> */} ); } render() { const { activeTab, allList, total } = this.props; - var filteredList; + // var filteredList; - if (activeTab.type !== ALL && activeTab.type !== 'bookmark' && activeTab.type !== 'live') { // Watchdog sessions - filteredList = allList.filter(session => activeTab.fits(session)) - } else { - filteredList = allList - } + // if (activeTab.type !== ALL && activeTab.type !== 'bookmark' && activeTab.type !== 'live') { // Watchdog sessions + // filteredList = allList.filter(session => activeTab.fits(session)) + // } else { + // filteredList = allList + // } - if (activeTab.type === 'bookmark') { - filteredList = filteredList.filter(item => item.favorite) - } - const _total = activeTab.type === 'all' ? total : filteredList.size + // if (activeTab.type === 'bookmark') { + // filteredList = filteredList.filter(item => item.favorite) + // } + // const _total = activeTab.type === 'all' ? total : allList.size return (
- - { this.renderActiveTabContent(filteredList) } + + { this.renderActiveTabContent(allList) }
); } diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx index 177dccf9a..0dfa6dbd3 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx @@ -12,7 +12,7 @@ function CustomMetriPercentage(props: Props) { return (
{numberWithCommas(data.count)}
-
{`${data.previousCount} ( ${data.countProgress}% )`}
+
{`${parseInt(data.previousCount).toFixed(1)} ( ${parseInt(data.countProgress).toFixed(1)}% )`}
from previous period.
) diff --git a/frontend/app/components/Session/Layout/PlayOverlay.js b/frontend/app/components/Session/Layout/PlayOverlay.js index 537460c26..3fb156172 100644 --- a/frontend/app/components/Session/Layout/PlayOverlay.js +++ b/frontend/app/components/Session/Layout/PlayOverlay.js @@ -1,8 +1,6 @@ import cn from 'classnames'; import { useCallback, useState } from 'react'; - import { Icon } from 'UI'; - import cls from './PlayOverlay.css'; export default function PlayOverlay({ player }) { @@ -11,20 +9,17 @@ export default function PlayOverlay({ player }) { const togglePlay = useCallback(() => { player.togglePlay(); setIconVisible(true); - setTimeout( - () => setIconVisible(false), - 800, - ); + setTimeout(() => setIconVisible(false), 800); }); return (
-
- -
-
+ className="absolute inset-0 flex items-center justify-center" + onClick={ togglePlay } + > +
+ +
+
); } diff --git a/frontend/app/components/Session/Layout/Player/Timeline.js b/frontend/app/components/Session/Layout/Player/Timeline.js index 5f05834ee..ca1383ffa 100644 --- a/frontend/app/components/Session/Layout/Player/Timeline.js +++ b/frontend/app/components/Session/Layout/Player/Timeline.js @@ -1,12 +1,9 @@ import { useCallback } from 'react'; import cn from 'classnames'; import { Popup } from 'UI'; - import { CRASHES, EVENTS } from 'Player/ios/state'; - import TimeTracker from './TimeTracker'; import PlayerTime from './PlayerTime'; - import cls from './timeline.css'; export default function Timeline({ player }) { @@ -19,7 +16,7 @@ export default function Timeline({ player }) { const time = Math.max(Math.round(p * player.state.endTime), 0); player.jump(time); }); - const scale = 100 / player.state.endTime; + const scale = 100 / player.state.endTime; return (
diff --git a/frontend/app/components/Session_/Player/Controls/Circle.tsx b/frontend/app/components/Session_/Player/Controls/Circle.tsx new file mode 100644 index 000000000..b49d23216 --- /dev/null +++ b/frontend/app/components/Session_/Player/Controls/Circle.tsx @@ -0,0 +1,18 @@ +import React, { memo, FC } from 'react'; +import styles from './timeline.css'; + +interface Props { + preview?: boolean; +} +export const Circle: FC = memo(function Box({ preview }) { + // const backgroundColor = yellow ? 'yellow' : 'white' + return ( +
+ ) + }) + +export default Circle; \ No newline at end of file diff --git a/frontend/app/components/Session_/Player/Controls/Controls.js b/frontend/app/components/Session_/Player/Controls/Controls.js index 22fd3b0cf..ab1baba3e 100644 --- a/frontend/app/components/Session_/Player/Controls/Controls.js +++ b/frontend/app/components/Session_/Player/Controls/Controls.js @@ -118,6 +118,7 @@ export default class Controls extends React.Component { componentDidMount() { document.addEventListener('keydown', this.onKeyDown); } + componentWillUnmount() { document.removeEventListener('keydown', this.onKeyDown); //this.props.toggleInspectorMode(false); @@ -166,10 +167,10 @@ export default class Controls extends React.Component { return; } if (this.props.inspectorMode) return; - if (e.key === ' ') { - document.activeElement.blur(); - this.props.togglePlay(); - } + // if (e.key === ' ') { + // document.activeElement.blur(); + // this.props.togglePlay(); + // } if (e.key === 'Esc' || e.key === 'Escape') { this.props.fullscreenOff(); } @@ -262,7 +263,7 @@ export default class Controls extends React.Component { return (
- { !live && } + { !live && } { !fullscreen &&
diff --git a/frontend/app/components/Session_/Player/Controls/CustomDragLayer.tsx b/frontend/app/components/Session_/Player/Controls/CustomDragLayer.tsx new file mode 100644 index 000000000..c72f03ce2 --- /dev/null +++ b/frontend/app/components/Session_/Player/Controls/CustomDragLayer.tsx @@ -0,0 +1,98 @@ +import React, { memo } from 'react'; +import { useDragLayer } from "react-dnd"; +import Circle from './Circle' +import type { CSSProperties, FC } from 'react' + +const layerStyles: CSSProperties = { + position: "fixed", + pointerEvents: "none", + zIndex: 100, + left: 0, + top: 0, + width: "100%", + height: "100%" + }; + +const ItemTypes = { + BOX: 'box', +} + +function getItemStyles(initialOffset, currentOffset, maxX, minX) { + if (!initialOffset || !currentOffset) { + return { + display: "none" + }; + } + let { x, y } = currentOffset; + // if (isSnapToGrid) { + // x -= initialOffset.x; + // y -= initialOffset.y; + // [x, y] = [x, y]; + // x += initialOffset.x; + // y += initialOffset.y; + // } + if (x > maxX) { + x = maxX; + } + + if (x < minX) { + x = minX; + } + const transform = `translate(${x}px, ${initialOffset.y}px)`; + return { + transition: 'transform 0.1s ease-out', + transform, + WebkitTransform: transform + }; +} + +interface Props { + onDrag: (offset: { x: number, y: number } | null) => void; + maxX: number; + minX: number; +} + +const CustomDragLayer: FC = memo(function CustomDragLayer(props) { + const { + itemType, + isDragging, + item, + initialOffset, + currentOffset, + } = useDragLayer((monitor) => ({ + item: monitor.getItem(), + itemType: monitor.getItemType(), + initialOffset: monitor.getInitialSourceClientOffset(), + currentOffset: monitor.getSourceClientOffset(), + isDragging: monitor.isDragging(), + })); + + function renderItem() { + switch (itemType) { + case ItemTypes.BOX: + return ; + default: + return null; + } + } + + if (!isDragging) { + return null; + } + + if (isDragging) { + props.onDrag(currentOffset) + } + + return ( +
+
+ {renderItem()} +
+
+ ); +}) + +export default CustomDragLayer; \ No newline at end of file diff --git a/frontend/app/components/Session_/Player/Controls/DraggableCircle.tsx b/frontend/app/components/Session_/Player/Controls/DraggableCircle.tsx new file mode 100644 index 000000000..9bdf37651 --- /dev/null +++ b/frontend/app/components/Session_/Player/Controls/DraggableCircle.tsx @@ -0,0 +1,67 @@ +import React, { memo, FC, useEffect, useRef, CSSProperties } from 'react'; +import type { DragSourceMonitor } from 'react-dnd' +import { useDrag } from 'react-dnd' +import { getEmptyImage } from 'react-dnd-html5-backend' +import Circle from './Circle' + +function getStyles( + left: number, + isDragging: boolean, + ): CSSProperties { + // const transform = `translate3d(${(left * 1161) / 100}px, -8px, 0)` + return { + position: 'absolute', + top: '-3px', + left: `${left}%`, + // transform, + // WebkitTransform: transform, + // IE fallback: hide the real node using CSS when dragging + // because IE will ignore our custom "empty image" drag preview. + opacity: isDragging ? 0 : 1, + height: isDragging ? 0 : '', + zIndex: '99999', + cursor: 'move' + } +} + +const ItemTypes = { + BOX: 'box', +} + +interface Props { + left: number; + top: number; + onDrop?: (item, monitor) => void; +} + +const DraggableCircle: FC = memo(function DraggableCircle(props) { + const { left, top } = props + const [{ isDragging, item }, dragRef, preview] = useDrag( + () => ({ + type: ItemTypes.BOX, + item: { left, top }, + end: props.onDrop, + collect: (monitor: DragSourceMonitor) => ({ + isDragging: monitor.isDragging(), + item: monitor.getItem(), + }), + }), + [left, top], + ) + + useEffect(() => { + preview(getEmptyImage(), { captureDraggingState: true }) + }, []) + + return ( +
+ +
+ ); +}) + +export default DraggableCircle \ No newline at end of file diff --git a/frontend/app/components/Session_/Player/Controls/TimeTracker.js b/frontend/app/components/Session_/Player/Controls/TimeTracker.js index be91f69fe..e3de669e5 100644 --- a/frontend/app/components/Session_/Player/Controls/TimeTracker.js +++ b/frontend/app/components/Session_/Player/Controls/TimeTracker.js @@ -4,10 +4,6 @@ import styles from './timeTracker.css'; const TimeTracker = ({ time, scale }) => ( -
{ // exception, @@ -51,6 +53,8 @@ const getPointerIcon = (type) => { } @connectPlayer(state => ({ + playing: state.playing, + time: state.time, skipIntervals: state.skipIntervals, events: state.eventList, skip: state.skip, @@ -72,6 +76,11 @@ const getPointerIcon = (type) => { state.getIn([ 'sessions', 'current', 'returningLocationTime' ]), }), { setTimelinePointer }) export default class Timeline extends React.PureComponent { + progressRef = React.createRef() + progressWidth = 0 + seekTime = 0 + wasPlaying = false + seekProgress = (e) => { const { endTime } = this.props; const p = e.nativeEvent.offsetX / e.target.offsetWidth; @@ -88,11 +97,32 @@ export default class Timeline extends React.PureComponent { componentDidMount() { const { issues, events, fetchList, skipToIssue } = this.props; const firstIssue = issues.get(0); + this.progressWidth = this.progressRef.current.offsetWidth; + if (firstIssue && skipToIssue) { this.props.jump(firstIssue.time); } } + onDragEnd = (item, monitor) => { + this.props.jump(this.seekTime); + if (this.wasPlaying) { + this.props.togglePlay(); + } + } + + onDrag = (offset) => { + const { endTime } = this.props; + + const p = (offset.x - 60) / this.progressRef.current.offsetWidth; + const time = Math.max(Math.round(p * endTime), 0); + this.seekTime = time; + if (this.props.playing) { + this.wasPlaying = true; + this.props.pause(); + } + } + render() { const { events, @@ -103,7 +133,7 @@ export default class Timeline extends React.PureComponent { live, logList, exceptionsList, - resourceList, + resourceList, clickRageTime, stackList, fetchList, @@ -111,12 +141,19 @@ export default class Timeline extends React.PureComponent { } = this.props; const scale = 100 / endTime; + return (
{ !live && } -
+
+ + { skip && skipIntervals.map(interval => (
{ + // TODO Find a better way to do this + document.addEventListener('keydown', onKeyDown); + + return () => { + document.removeEventListener('keydown', onKeyDown); + } + }, []) + + const onKeyDown = (e) => { + if (e.key === ' ') { + togglePlayAnimated() + } + } + const togglePlayAnimated = useCallback(() => { setShowPlayOverlayIcon(true); togglePlay(); - setTimeout( - () => setShowPlayOverlayIcon(false), - 800, - ); + setTimeout(() => setShowPlayOverlayIcon(false), 800); }, []); + return (
{ return { type: REFRESH_FILTER_OPTIONS } +} + +export const setScrollPosition = (scrollPosition) => { + return { + type: SET_SCROLL_POSITION, + scrollPosition, + } } \ No newline at end of file diff --git a/frontend/app/initialize.js b/frontend/app/initialize.js index dcfd01058..874bd0586 100644 --- a/frontend/app/initialize.js +++ b/frontend/app/initialize.js @@ -1,13 +1,9 @@ import './init'; - import { render } from 'react-dom'; import { Provider } from 'react-redux'; - import store from './store'; import Router from './Router'; 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' @@ -17,10 +13,7 @@ document.addEventListener('DOMContentLoaded', () => { - {/* */} - {/* */} - {/* */} diff --git a/frontend/app/svg/icons/pie-chart-fill.svg b/frontend/app/svg/icons/pie-chart-fill.svg index 6aa71eb89..e3e67bb5c 100644 --- a/frontend/app/svg/icons/pie-chart-fill.svg +++ b/frontend/app/svg/icons/pie-chart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file From 43f0c50e2d37c3eb6e2fb56c0d814f787a184fd7 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 12 Apr 2022 12:45:06 +0200 Subject: [PATCH 119/294] change(ui) - css --- .../app/components/Session_/Player/Controls/timeline.css | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/app/components/Session_/Player/Controls/timeline.css b/frontend/app/components/Session_/Player/Controls/timeline.css index 4573afb23..da7a99cd8 100644 --- a/frontend/app/components/Session_/Player/Controls/timeline.css +++ b/frontend/app/components/Session_/Player/Controls/timeline.css @@ -1,9 +1,6 @@ -@import 'zindex.css'; - .positionTracker { width: 15px; height: 15px; - /* border: solid 1px $teal; */ outline: solid 1px $teal; outline-style: inset; margin-left: -7px; @@ -11,7 +8,7 @@ background-color: $active-blue; position: absolute; left: 0; - z-index: $positionTracker; + z-index: 98; top: 0; transition: all 0.2s ease-out; &:hover, From fc2336bc91244dc3886746170278ef45da5a0d35 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 12 Apr 2022 12:55:29 +0200 Subject: [PATCH 120/294] fix(ui) - metric selection height --- .../DashboardMetricSelection/DashboardMetricSelection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index e0b4778c4..371815135 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -89,7 +89,7 @@ function DashboardMetricSelection(props) {
{activeCategory && activeCategory.widgets.map((widget: any) => ( Date: Tue, 12 Apr 2022 16:25:31 +0200 Subject: [PATCH 121/294] feat(ui) - dashboard - wip --- .../app/components/Dashboard/NewDashboard.tsx | 18 +++++----- .../CustomMetricOverviewChart.tsx | 2 +- .../DashboardRouter/DashboardRouter.tsx | 16 ++++++--- .../DashboardSideMenu/DashboardSideMenu.tsx | 2 +- .../DashboardView/DashboardView.tsx | 22 +++++++++--- .../DashboardWidgetGrid.tsx | 2 +- .../app/components/Header/SiteDropdown.js | 7 ++++ .../app/components/ui/NoContent/NoContent.js | 4 ++- frontend/app/mstore/dashboardStore.ts | 36 +++++++++---------- frontend/app/mstore/types/widget.ts | 1 - frontend/app/svg/icons/no-metrics-chart.svg | 7 ++++ 11 files changed, 76 insertions(+), 41 deletions(-) create mode 100644 frontend/app/svg/icons/no-metrics-chart.svg diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index f66dbe4de..ca62377f3 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -20,15 +20,17 @@ function NewDashboard(props) { dashboardStore.fetchList().then((resp) => { if (parseInt(dashboardId) > 0) { dashboardStore.selectDashboardById(dashboardId); - } else { - dashboardStore.selectDefaultDashboard().then(({ dashboardId }) => { - if (!history.location.pathname.includes('/metrics')) { - history.push(withSiteId(dashboardSelected(dashboardId), siteId)); - } - }); - } + } + // else { + // dashboardStore.selectDefaultDashboard().then(({ dashboardId }) => { + // console.log('dashboardId', dashboardId) + // // if (!history.location.pathname.includes('/metrics')) { + // // history.push(withSiteId(dashboardSelected(dashboardId), siteId)); + // // } + // }); + // } }); - }, []); + }, [siteId]); return ( diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx index 93e0fbf6d..769e63fb4 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx @@ -12,8 +12,8 @@ interface Props { } function CustomMetricOverviewChart(props: Props) { const { data } = props; - console.log('data', data) const gradientDef = Styles.gradientDef(); + return (
diff --git a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx index 9621f1c71..6004202a2 100644 --- a/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx +++ b/frontend/app/components/Dashboard/components/DashboardRouter/DashboardRouter.tsx @@ -15,6 +15,12 @@ import DashboardView from '../DashboardView'; import MetricsView from '../MetricsView'; import WidgetView from '../WidgetView'; +function DashboardViewSelected({ siteId, dashboardId}) { + return ( + + ) +} + interface Props { history: any match: any @@ -32,6 +38,10 @@ function DashboardRouter(props: Props) { + + + + @@ -40,12 +50,8 @@ 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 3d1901be2..838d0930d 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 { history, siteId } = props; const { hideModal, showModal } = useModal(); const { dashboardStore } = useStore(); - const dashboardId = dashboardStore.selectedDashboard?.dashboardId; + const dashboardId = useObserver(() => dashboardStore.selectedDashboard?.dashboardId); const dashboardsPicked = useObserver(() => dashboardStore.dashboards.slice(0, SHOW_COUNT)); const remainingDashboardsCount = dashboardStore.dashboards.length - SHOW_COUNT; diff --git a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx index 5d698f704..b741c009b 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -23,12 +23,19 @@ function DashboardView(props: Props) { const { dashboardStore } = useStore(); const { hideModal, showModal } = useModal(); const loading = useObserver(() => dashboardStore.fetchingDashboard); - const dashboard: any = dashboardStore.selectedDashboard + const dashboards = useObserver(() => dashboardStore.dashboards); + const dashboard: any = useObserver(() => dashboardStore.selectedDashboard); const period = useObserver(() => dashboardStore.period); const [showEditModal, setShowEditModal] = React.useState(false); useEffect(() => { - dashboardStore.fetch(dashboardId) + if (!dashboard || !dashboard.dashboardId) return; + dashboardStore.fetch(dashboard.dashboardId) + }, [dashboard]); + + useEffect(() => { + if (dashboardId) return; + dashboardStore.selectDefaultDashboard(); }, []); const onAddWidgets = () => { @@ -58,9 +65,14 @@ function DashboardView(props: Props) { return useObserver(() => ( Create Dashboard + } >
diff --git a/frontend/app/components/Header/SiteDropdown.js b/frontend/app/components/Header/SiteDropdown.js index 74b650055..4f8b9ca60 100644 --- a/frontend/app/components/Header/SiteDropdown.js +++ b/frontend/app/components/Header/SiteDropdown.js @@ -13,7 +13,9 @@ import { clearSearch } from 'Duck/search'; import { fetchList as fetchIntegrationVariables } from 'Duck/customField'; import { fetchList as fetchAlerts } from 'Duck/alerts'; import { fetchWatchdogStatus } from 'Duck/watchdogs'; +import { withStore } from 'App/mstore' +@withStore @withRouter @connect(state => ({ sites: state.getIn([ 'site', 'list' ]), @@ -45,11 +47,16 @@ export default class SiteDropdown extends React.PureComponent { } switchSite = (siteId) => { + const { mstore } = this.props + + this.props.setSiteId(siteId); this.props.clearSearch(); this.props.fetchIntegrationVariables(); this.props.fetchAlerts(); this.props.fetchWatchdogStatus(); + + mstore.initClient(); } render() { diff --git a/frontend/app/components/ui/NoContent/NoContent.js b/frontend/app/components/ui/NoContent/NoContent.js index 83b4f53f3..846627260 100644 --- a/frontend/app/components/ui/NoContent/NoContent.js +++ b/frontend/app/components/ui/NoContent/NoContent.js @@ -5,6 +5,7 @@ export default ({ title = "No data available.", subtext, icon, + iconSize = 100, size, show = true, children = null, @@ -14,7 +15,8 @@ export default ({ }) => (!show ? children :
{ - icon &&
+ // icon &&
+ icon && } { title &&
{ title }
} { diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 6739d54b6..ced5de480 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -181,7 +181,8 @@ export default class DashboardStore implements IDashboardSotre { fetch(dashboardId: string): Promise { this.fetchingDashboard = true return dashboardService.getDashboard(dashboardId).then(response => { - this.selectedDashboard = new Dashboard().fromJson(response) + // const widgets = new Dashboard().fromJson(response).widgets + this.selectedDashboard?.update({ 'widgets' : new Dashboard().fromJson(response).widgets}) }).finally(() => { this.fetchingDashboard = false }) @@ -300,9 +301,9 @@ export default class DashboardStore implements IDashboardSotre { selectDashboardById = (dashboardId: any) => { this.selectedDashboard = this.dashboards.find(d => d.dashboardId == dashboardId) || new Dashboard(); - if (this.selectedDashboard.dashboardId) { - this.fetch(this.selectedDashboard.dashboardId) - } + // if (this.selectedDashboard.dashboardId) { + // this.fetch(this.selectedDashboard.dashboardId) + // } } setSiteId = (siteId: any) => { @@ -418,16 +419,16 @@ export default class DashboardStore implements IDashboardSotre { // } 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; + // 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 = { ...data, namesMap: data.namesMap, chart: data.chart } // metric.setData(_data) @@ -447,9 +448,8 @@ export default class DashboardStore implements IDashboardSotre { return unique; }, []) } else { - _data['chart'] = data - _data['namesMap'] = data - .map(i => Object.keys(i)) + _data['chart'] = Array.isArray(data) ? data : [] + _data['namesMap'] = Array.isArray(data) ? data.map(i => Object.keys(i)) .flat() .filter(i => i !== 'time' && i !== 'timestamp') .reduce((unique: any, item: any) => { @@ -457,7 +457,7 @@ export default class DashboardStore implements IDashboardSotre { unique.push(item); } return unique; - }, []) + }, []) : [] } metric.setData(_data) diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index c32b78ed2..ee9c559b4 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -130,7 +130,6 @@ export default class Widget implements IWidget { this.position = json.config.position this.predefinedKey = json.predefinedKey }) - console.log(this.name, this.position) return this } diff --git a/frontend/app/svg/icons/no-metrics-chart.svg b/frontend/app/svg/icons/no-metrics-chart.svg new file mode 100644 index 000000000..74c3b7b2d --- /dev/null +++ b/frontend/app/svg/icons/no-metrics-chart.svg @@ -0,0 +1,7 @@ + + + + + + + From 53063f78470169f6c830461656dffcb1d08eea6d Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 12 Apr 2022 16:49:45 +0200 Subject: [PATCH 122/294] change(ui) - no content icon --- .../app/components/Alerts/Notifications/Notifications.js | 2 +- frontend/app/components/Announcements/Announcements.js | 2 +- .../BugFinder/SessionFlowList/SessionFlowList.js | 2 +- .../app/components/BugFinder/SessionList/SessionList.js | 2 +- frontend/app/components/Dashboard/Dashboard.js | 3 +-- .../Dashboard/components/MetricsList/MetricsList.tsx | 2 +- .../Dashboard/components/WidgetSessions/WidgetSessions.tsx | 2 +- .../Dashboard/components/WidgetWrapper/WidgetWrapper.tsx | 7 +------ frontend/app/components/Errors/Error/ErrorInfo.js | 2 +- .../app/components/Funnels/FunnelIssues/FunnelIssues.js | 2 +- .../Funnels/FunnelSessionList/FunnelSessionList.js | 2 +- frontend/app/components/Session_/Fetch/FetchDetails.js | 4 ++-- .../Session_/Fetch/components/Headers/Headers.tsx | 2 +- .../CustomMetrics/SessionListModal/SessionListModal.tsx | 2 +- .../app/components/shared/ResultTimings/ResultTimings.js | 2 +- frontend/app/components/ui/NoContent/NoContent.js | 3 ++- frontend/app/components/ui/NoContent/noContent.css | 4 ++-- 17 files changed, 20 insertions(+), 25 deletions(-) diff --git a/frontend/app/components/Alerts/Notifications/Notifications.js b/frontend/app/components/Alerts/Notifications/Notifications.js index a30ad824f..b4dd055a0 100644 --- a/frontend/app/components/Alerts/Notifications/Notifications.js +++ b/frontend/app/components/Alerts/Notifications/Notifications.js @@ -113,7 +113,7 @@ class Notifications extends React.Component { diff --git a/frontend/app/components/Announcements/Announcements.js b/frontend/app/components/Announcements/Announcements.js index 733b4ce37..e0d09f024 100644 --- a/frontend/app/components/Announcements/Announcements.js +++ b/frontend/app/components/Announcements/Announcements.js @@ -71,7 +71,7 @@ class Announcements extends React.Component { diff --git a/frontend/app/components/BugFinder/SessionFlowList/SessionFlowList.js b/frontend/app/components/BugFinder/SessionFlowList/SessionFlowList.js index c7dca4cf8..f4962573a 100644 --- a/frontend/app/components/BugFinder/SessionFlowList/SessionFlowList.js +++ b/frontend/app/components/BugFinder/SessionFlowList/SessionFlowList.js @@ -11,7 +11,7 @@ function SessionFlowList({ activeTab, savedFilters, loading }) { diff --git a/frontend/app/components/BugFinder/SessionList/SessionList.js b/frontend/app/components/BugFinder/SessionList/SessionList.js index 8ec60a884..938f15b30 100644 --- a/frontend/app/components/BugFinder/SessionList/SessionList.js +++ b/frontend/app/components/BugFinder/SessionList/SessionList.js @@ -97,7 +97,7 @@ export default class SessionList extends React.PureComponent { diff --git a/frontend/app/components/Dashboard/Dashboard.js b/frontend/app/components/Dashboard/Dashboard.js index 2b71ef253..9d84a8779 100644 --- a/frontend/app/components/Dashboard/Dashboard.js +++ b/frontend/app/components/Dashboard/Dashboard.js @@ -212,8 +212,7 @@ export default class Dashboard extends React.PureComponent { show={ noWidgets } title="You haven't added any insights widgets!" subtext="Add new to keep track of Processed Sessions, Application Activity, Errors and lot more." - icon - empty + animatedIcon="empty-state" > ( - +
Title
diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index cb39f7d3d..978fad1fb 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -23,7 +23,7 @@ function WidgetSessions(props: Props) { {widget.sessions.map((session: any) => ( diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index b27175c1a..bb47dbc98 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -61,10 +61,6 @@ function WidgetWrapper(props: Props) { } } - const editHandler = () => { - console.log('clicked', widget.metricId); - } - const onChartClick = () => { if (!isWidget || widget.metricType === 'predefined') return; props.history.push(withSiteId(dashboardMetricDetails(dashboardId, widget.metricId),siteId)); @@ -88,14 +84,13 @@ function WidgetWrapper(props: Props) {
-

{widget.name}

{isWidget && (
diff --git a/frontend/app/components/Funnels/FunnelIssues/FunnelIssues.js b/frontend/app/components/Funnels/FunnelIssues/FunnelIssues.js index 82d39037c..b46ef824d 100644 --- a/frontend/app/components/Funnels/FunnelIssues/FunnelIssues.js +++ b/frontend/app/components/Funnels/FunnelIssues/FunnelIssues.js @@ -44,7 +44,7 @@ function FunnelIssues(props) { { filteredList.take(displayedCount).map(issue => ( diff --git a/frontend/app/components/Funnels/FunnelSessionList/FunnelSessionList.js b/frontend/app/components/Funnels/FunnelSessionList/FunnelSessionList.js index 707049faa..f1b3ec67a 100644 --- a/frontend/app/components/Funnels/FunnelSessionList/FunnelSessionList.js +++ b/frontend/app/components/Funnels/FunnelSessionList/FunnelSessionList.js @@ -30,7 +30,7 @@ function FunnelSessionList(props) { { list.take(displayedCount).map(session => ( diff --git a/frontend/app/components/Session_/Fetch/FetchDetails.js b/frontend/app/components/Session_/Fetch/FetchDetails.js index b7a5386a1..6893c7194 100644 --- a/frontend/app/components/Session_/Fetch/FetchDetails.js +++ b/frontend/app/components/Session_/Fetch/FetchDetails.js @@ -44,7 +44,7 @@ export default class FetchDetails extends React.PureComponent { title="Body is Empty." size="small" show={ !payload } - icon="exclamation-circle" + animatedIcon="no-results" >
@@ -63,7 +63,7 @@ export default class FetchDetails extends React.PureComponent { title="Body is Empty." size="small" show={ !response } - icon="exclamation-circle" + animatedIcon="no-results" >
diff --git a/frontend/app/components/Session_/Fetch/components/Headers/Headers.tsx b/frontend/app/components/Session_/Fetch/components/Headers/Headers.tsx index fa941e36f..47ebff217 100644 --- a/frontend/app/components/Session_/Fetch/components/Headers/Headers.tsx +++ b/frontend/app/components/Session_/Fetch/components/Headers/Headers.tsx @@ -9,7 +9,7 @@ function Headers(props) { title="No data available." size="small" show={ !props.requestHeaders && !props.responseHeaders } - icon="exclamation-circle" + animatedIcon="no-results" > { props.requestHeaders && ( <> diff --git a/frontend/app/components/shared/CustomMetrics/SessionListModal/SessionListModal.tsx b/frontend/app/components/shared/CustomMetrics/SessionListModal/SessionListModal.tsx index 7da36c6a9..921795f97 100644 --- a/frontend/app/components/shared/CustomMetrics/SessionListModal/SessionListModal.tsx +++ b/frontend/app/components/shared/CustomMetrics/SessionListModal/SessionListModal.tsx @@ -101,7 +101,7 @@ function SessionListModal(props: Props) { { filteredSessions.map(session => ) } diff --git a/frontend/app/components/shared/ResultTimings/ResultTimings.js b/frontend/app/components/shared/ResultTimings/ResultTimings.js index 2cd82ee1e..120233074 100644 --- a/frontend/app/components/shared/ResultTimings/ResultTimings.js +++ b/frontend/app/components/shared/ResultTimings/ResultTimings.js @@ -24,7 +24,7 @@ function ResultTimings({ duration, timing }) { return ( diff --git a/frontend/app/components/ui/NoContent/NoContent.js b/frontend/app/components/ui/NoContent/NoContent.js index 846627260..ed5d63828 100644 --- a/frontend/app/components/ui/NoContent/NoContent.js +++ b/frontend/app/components/ui/NoContent/NoContent.js @@ -4,6 +4,7 @@ import styles from './noContent.css'; export default ({ title = "No data available.", subtext, + animatedIcon = false, icon, iconSize = 100, size, @@ -16,7 +17,7 @@ export default ({
{ // icon &&
- icon && + animatedIcon ?
: (icon && ) } { title &&
{ title }
} { diff --git a/frontend/app/components/ui/NoContent/noContent.css b/frontend/app/components/ui/NoContent/noContent.css index f4296757c..5cf7a0d24 100644 --- a/frontend/app/components/ui/NoContent/noContent.css +++ b/frontend/app/components/ui/NoContent/noContent.css @@ -34,7 +34,7 @@ } -.icon { +.no-results { display: block; margin: auto; background-image: svg-load(no-results.svg, fill=#CCC); @@ -46,7 +46,7 @@ margin-bottom: 20px; } -.emptyIcon { +.empty-state { display: block; margin: auto; background-image: svg-load(empty-state.svg, fill=#CCC); From d50d63ba7e331c7fd7eab50ff6e41f02ebc24966 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 12 Apr 2022 17:37:06 +0200 Subject: [PATCH 123/294] change(ui) - assist filter box --- .../BugFinder/SessionList/SessionList.js | 1 - .../WidgetPreview/WidgetPreview.tsx | 16 +++++------- .../WidgetSessions/WidgetSessions.tsx | 16 ++++++++---- .../LiveSessionSearch/LiveSessionSearch.tsx | 25 +++++++++++-------- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/frontend/app/components/BugFinder/SessionList/SessionList.js b/frontend/app/components/BugFinder/SessionList/SessionList.js index 938f15b30..64bf722f4 100644 --- a/frontend/app/components/BugFinder/SessionList/SessionList.js +++ b/frontend/app/components/BugFinder/SessionList/SessionList.js @@ -74,7 +74,6 @@ export default class SessionList extends React.PureComponent { componentDidMount() { const { scrollY } = this.props; - console.log('scrollY', scrollY); window.scrollTo(0, scrollY); } diff --git a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx index 4ec8a63e2..24bfdcbd8 100644 --- a/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPreview/WidgetPreview.tsx @@ -11,7 +11,8 @@ interface Props { } function WidgetPreview(props: Props) { const { className = '' } = props; - const { metricStore } = useStore(); + const { metricStore, dashboardStore } = useStore(); + const period = useObserver(() => dashboardStore.period); const metric: any = useObserver(() => metricStore.instance); const isTimeSeries = metric.metricType === 'timeseries'; const isTable = metric.metricType === 'table'; @@ -20,11 +21,6 @@ function WidgetPreview(props: Props) { metric.update({ [ name ]: value }); } - const onDateChange = (changedDates) => { - // setPeriod({ ...changedDates, rangeName: changedDates.rangeValue }) - metric.update({ ...changedDates, rangeName: changedDates.rangeValue }); - } - return useObserver(() => (
@@ -68,10 +64,10 @@ function WidgetPreview(props: Props) {
Time Range dashboardStore.setPeriod(period)} customRangeRight direction="left" /> diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index 978fad1fb..0ceace3c2 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -3,20 +3,26 @@ import { NoContent } from 'UI'; import cn from 'classnames'; import { useStore } from 'App/mstore'; import SessionItem from 'Shared/SessionItem'; - +import { useObserver } from 'mobx-react-lite'; +import { DateTime } from 'luxon'; interface Props { className?: string; } function WidgetSessions(props: Props) { const { className = '' } = props; const { dashboardStore } = useStore(); + const period = useObserver(() => dashboardStore.period); const widget = dashboardStore.currentWidget; - return ( + const range = period.toTimestamps() + const startTime = DateTime.fromMillis(range.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + const endTime = DateTime.fromMillis(range.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + + return useObserver(() => (
-
+

Sessions

- {/*
Showing all sessions between {startTime} and {endTime}
*/} +
between {startTime} and {endTime}
@@ -31,7 +37,7 @@ function WidgetSessions(props: Props) {
- ); + )); } export default WidgetSessions; \ No newline at end of file diff --git a/frontend/app/components/shared/LiveSessionSearch/LiveSessionSearch.tsx b/frontend/app/components/shared/LiveSessionSearch/LiveSessionSearch.tsx index 5f6e5f7cc..cc3fef487 100644 --- a/frontend/app/components/shared/LiveSessionSearch/LiveSessionSearch.tsx +++ b/frontend/app/components/shared/LiveSessionSearch/LiveSessionSearch.tsx @@ -4,9 +4,9 @@ import { connect } from 'react-redux'; import { edit, addFilter, addFilterByKeyAndValue } from 'Duck/liveSearch'; import FilterSelection from 'Shared/Filters/FilterSelection'; import { IconButton } from 'UI'; -import { FilterKey } from 'App/types/filter/filterType'; interface Props { + list: any, appliedFilter: any; edit: typeof edit; addFilter: typeof addFilter; @@ -53,16 +53,18 @@ function LiveSessionSearch(props: Props) { }); } - return ( + return props.list.size > 0 ? (
-
- -
+ { hasEvents || hasFilters && ( +
+ +
+ )}
@@ -75,9 +77,10 @@ function LiveSessionSearch(props: Props) {
- ); + ) : <>; } export default connect(state => ({ appliedFilter: state.getIn([ 'liveSearch', 'instance' ]), + list: state.getIn(['sessions', 'liveSessions']), }), { edit, addFilter, addFilterByKeyAndValue })(LiveSessionSearch); \ No newline at end of file From b7dc03539b8f771d4dff735e71f516407e0166bb Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 12 Apr 2022 18:13:31 +0200 Subject: [PATCH 124/294] feat(utilities): EE cluster-ws fixes --- ee/utilities/servers/websocket-cluster.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ee/utilities/servers/websocket-cluster.js b/ee/utilities/servers/websocket-cluster.js index 8dc3bf94c..e2e6aaa86 100644 --- a/ee/utilities/servers/websocket-cluster.js +++ b/ee/utilities/servers/websocket-cluster.js @@ -87,8 +87,7 @@ const extractProjectKeyFromRequest = function (req) { const getAvailableRooms = async function () { - let rooms = await io.of('/').adapter.allRooms(); - return rooms; + return io.of('/').adapter.allRooms(); } const respond = function (res, data) { @@ -175,7 +174,7 @@ const socketsLive = async function (req, res) { } } } - liveSessions[projectKey] = uniqueSessions(liveSessions[_projectKey]); + liveSessions[projectKey] = uniqueSessions(liveSessions[projectKey]); } } respond(res, liveSessions); @@ -204,7 +203,7 @@ const socketsLiveByProject = async function (req, res) { } } } - liveSessions[projectKey] = uniqueSessions(liveSessions[_projectKey]); + liveSessions[projectKey] = uniqueSessions(liveSessions[projectKey] || []); } } respond(res, liveSessions[_projectKey] || []); From e20b0d4f68b9c8eb14279a1986f0c9a76d8ad0a1 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 12 Apr 2022 19:20:26 +0200 Subject: [PATCH 125/294] feat(api): use BackgroundTasks to flag viewed sessions feat(api): use BackgroundTasks to flag viewed errors --- api/routers/core.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/api/routers/core.py b/api/routers/core.py index 06743c054..813577b88 100644 --- a/api/routers/core.py +++ b/api/routers/core.py @@ -1,7 +1,7 @@ from typing import Union from decouple import config -from fastapi import Depends, Body +from fastapi import Depends, Body, BackgroundTasks import schemas from chalicelib.core import log_tool_rollbar, sourcemaps, events, sessions_assignments, projects, \ @@ -23,7 +23,8 @@ 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)): +def get_session2(projectId: int, sessionId: Union[int, str], background_tasks: BackgroundTasks, + context: schemas.CurrentContext = Depends(OR_context)): if isinstance(sessionId, str): return {"errors": ["session not found"]} data = sessions.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True, user_id=context.user_id, @@ -31,7 +32,8 @@ def get_session2(projectId: int, sessionId: Union[int, str], context: schemas.Cu 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) + background_tasks.add_task(sessions_favorite_viewed.view_session, project_id=projectId, user_id=context.user_id, + session_id=sessionId) return { 'data': data } @@ -833,7 +835,8 @@ def sessions_live(projectId: int, userId: str = None, context: schemas.CurrentCo @app.get('/{projectId}/assist/sessions/{sessionId}', tags=["assist"]) -def get_live_session(projectId: int, sessionId: str, context: schemas.CurrentContext = Depends(OR_context)): +def get_live_session(projectId: int, sessionId: str, background_tasks: BackgroundTasks, + 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, @@ -841,7 +844,8 @@ def get_live_session(projectId: int, sessionId: str, context: schemas.CurrentCon 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) + background_tasks.add_task(sessions_favorite_viewed.view_session, project_id=projectId, + user_id=context.user_id, session_id=sessionId) return {'data': data} @@ -909,12 +913,14 @@ def errors_stats(projectId: int, startTimestamp: int, endTimestamp: int, @app.get('/{projectId}/errors/{errorId}', tags=['errors']) -def errors_get_details(projectId: int, errorId: str, density24: int = 24, density30: int = 30, +def errors_get_details(projectId: int, errorId: str, background_tasks: BackgroundTasks, density24: int = 24, + density30: int = 30, context: schemas.CurrentContext = Depends(OR_context)): data = errors.get_details(project_id=projectId, user_id=context.user_id, error_id=errorId, **{"density24": density24, "density30": density30}) if data.get("data") is not None: - errors_favorite_viewed.viewed_error(project_id=projectId, user_id=context.user_id, error_id=errorId) + background_tasks.add_task(errors_favorite_viewed.viewed_error, project_id=projectId, user_id=context.user_id, + error_id=errorId) return data From e31dd82b92b90c66fd8e2e6e5f9d205a3151beb1 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 12 Apr 2022 19:44:34 +0200 Subject: [PATCH 126/294] feat(api): optimized get session replay --- api/chalicelib/core/resources.py | 10 +++++----- api/chalicelib/core/sessions.py | 2 +- ee/api/chalicelib/core/resources.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/chalicelib/core/resources.py b/api/chalicelib/core/resources.py index 6a7e395f8..d85e56b6f 100644 --- a/api/chalicelib/core/resources.py +++ b/api/chalicelib/core/resources.py @@ -1,23 +1,23 @@ from chalicelib.utils import helper, pg_client -def get_by_session_id(session_id): +def get_by_session_id(session_id, project_id): with pg_client.PostgresClient() as cur: ch_query = """\ SELECT timestamp AS datetime, url, type, - duration, + resources.duration AS duration, ttfb, header_size, encoded_body_size, decoded_body_size, success, COALESCE(status, CASE WHEN success THEN 200 END) AS status - FROM events.resources - WHERE session_id = %(session_id)s;""" - params = {"session_id": session_id} + FROM events.resources INNER JOIN sessions USING (session_id) + WHERE session_id = %(session_id)s AND project_id= %(project_id)s;""" + params = {"session_id": session_id, "project_id": project_id} cur.execute(cur.mogrify(ch_query, params)) rows = cur.fetchall() return helper.list_to_camel_case(rows) diff --git a/api/chalicelib/core/sessions.py b/api/chalicelib/core/sessions.py index 83cad2ad7..c0fdf57bd 100644 --- a/api/chalicelib/core/sessions.py +++ b/api/chalicelib/core/sessions.py @@ -94,7 +94,7 @@ def get_by_id2_pg(project_id, session_id, user_id, full_data=False, include_fav_ data['userEvents'] = events.get_customs_by_sessionId2_pg(project_id=project_id, session_id=session_id) data['mobsUrl'] = sessions_mobs.get_web(sessionId=session_id) - data['resources'] = resources.get_by_session_id(session_id=session_id) + data['resources'] = resources.get_by_session_id(session_id=session_id, project_id=project_id) data['metadata'] = __group_metadata(project_metadata=data.pop("projectMetadata"), session=data) data['issues'] = issues.get_by_session_id(session_id=session_id) diff --git a/ee/api/chalicelib/core/resources.py b/ee/api/chalicelib/core/resources.py index 332d3709a..4e4f1c4e8 100644 --- a/ee/api/chalicelib/core/resources.py +++ b/ee/api/chalicelib/core/resources.py @@ -3,14 +3,14 @@ from chalicelib.utils import ch_client from chalicelib.utils.TimeUTC import TimeUTC -def get_by_session_id(session_id): +def get_by_session_id(session_id, project_id): with ch_client.ClickHouseClient() as ch: ch_query = """\ SELECT datetime,url,type,duration,ttfb,header_size,encoded_body_size,decoded_body_size,success,coalesce(status,if(success, 200, status)) AS status FROM resources - WHERE session_id = toUInt64(%(session_id)s);""" - params = {"session_id": session_id} + WHERE session_id = toUInt64(%(session_id)s) AND project_id=%(project_id)s;""" + params = {"session_id": session_id, "project_id": project_id} rows = ch.execute(query=ch_query, params=params) results = [] for r in rows: From d285310852537580daa753e1be3cd2b97ce328b6 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 12 Apr 2022 19:47:09 +0200 Subject: [PATCH 127/294] feat(utilities): protected heap-snapshot endpoints --- utilities/utils/HeapSnapshot.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utilities/utils/HeapSnapshot.js b/utilities/utils/HeapSnapshot.js index 23708f07c..dc0b45e9a 100644 --- a/utilities/utils/HeapSnapshot.js +++ b/utilities/utils/HeapSnapshot.js @@ -57,7 +57,7 @@ function createNewHeapSnapshot(req, res) { res.end(JSON.stringify({path: location + fileName, 'done': creationStatus})); } -router.get('/status', getHeapSnapshotStatus); -router.get(`/new`, createNewHeapSnapshot); -router.get(`/download`, downloadHeapSnapshot); +router.get(`/${process.env.S3_KEY}/status`, getHeapSnapshotStatus); +router.get(`/${process.env.S3_KEY}/new`, createNewHeapSnapshot); +router.get(`/${process.env.S3_KEY}/download`, downloadHeapSnapshot); module.exports = {router} \ No newline at end of file From 98109ee9a3c05387c41ce0a354ca30eed86c62b9 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 12:34:40 +0200 Subject: [PATCH 128/294] feat(api): dashboard added chart to avg_dom_content_load_start --- api/chalicelib/core/dashboard.py | 54 ++++++++++++++++++++++------- ee/api/chalicelib/core/dashboard.py | 34 ++++++++++++++++++ 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 362e4c491..b027beb87 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -1483,7 +1483,7 @@ def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), cur.execute(cur.mogrify(pg_query, params)) avg = cur.fetchone()["avg"] return {"value": avg, "chart": helper.list_to_camel_case(rows), - "unit": schemas.TemplatePredefinedUnits.percentage} + "unit": schemas.TemplatePredefinedUnits.percentage} def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -2426,12 +2426,7 @@ def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now 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, @@ -2470,16 +2465,16 @@ def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now 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]) + row = __get_page_metrics_avg_dom_content_load_start(cur, project_id, startTimestamp, endTimestamp, **args) + results = helper.dict_to_camel_case(row) + results["chart"] = __get_page_metrics_avg_dom_content_load_start_chart(cur, project_id, startTimestamp, + endTimestamp, **args) 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"]) + row = __get_page_metrics_avg_dom_content_load_start(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 @@ -2498,6 +2493,39 @@ def __get_page_metrics_avg_dom_content_load_start(cur, project_id, startTimestam 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_page_metrics_avg_dom_content_load_start_chart(cur, project_id, startTimestamp, endTimestamp, density=19, + **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + 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_sub_query_subset.append("pages.dom_content_loaded_time > 0") + + pg_query = f"""WITH pages AS(SELECT pages.dom_content_loaded_time, pages.timestamp + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_subset)} + ) + SELECT generated_timestamp AS timestamp, + COALESCE(AVG(pages.dom_content_loaded_time),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT pages.dom_content_loaded_time + FROM pages + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp;""" + cur.execute(cur.mogrify(pg_query, {**params, **__get_constraint_values(args)})) rows = cur.fetchall() return rows diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index c36c877da..9e56cc351 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -1,6 +1,7 @@ import math import random +import schemas from chalicelib.utils import pg_client from chalicelib.utils import args_transformer from chalicelib.utils import helper @@ -2322,9 +2323,12 @@ def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now 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: + results = {} 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]) + results["chart"] = __get_page_metrics_avg_dom_content_load_start_chart(ch, project_id, startTimestamp, + endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2332,6 +2336,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 @@ -2349,6 +2354,35 @@ def __get_page_metrics_avg_dom_content_load_start(ch, project_id, startTimestamp return rows +def __get_page_metrics_avg_dom_content_load_start_chart(ch, project_id, startTimestamp, endTimestamp, density=19, + **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + AVG(NULLIF(pages.dom_content_loaded_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)} + GROUP BY timestamp + ORDER BY timestamp;""" + rows = ch.execute(query=ch_query, params={**params, **__get_constraint_values(args)}) + rows = [{"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 rows: + for k in s: + if s[k] is None: + s[k] = 0 + 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: From ae9580b34521773dae4d1d932f690a5182bda50f Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 12:42:04 +0200 Subject: [PATCH 129/294] feat(api): dashboard user single DB-connexion for charts and data of the same widget --- api/chalicelib/core/dashboard.py | 166 ++++++++++++++-------------- ee/api/chalicelib/core/dashboard.py | 127 +++++++++++---------- 2 files changed, 145 insertions(+), 148 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index b027beb87..1fb526d96 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2265,7 +2265,7 @@ def get_application_activity_avg_image_load_time(project_id, startTimestamp=Time with pg_client.PostgresClient() as cur: row = __get_application_activity_avg_image_load_time(cur, project_id, startTimestamp, endTimestamp, **args) results = row - results["chart"] = get_performance_avg_image_load_time(project_id, startTimestamp, endTimestamp, **args) + results["chart"] = get_performance_avg_image_load_time(cur, project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2276,7 +2276,7 @@ def get_application_activity_avg_image_load_time(project_id, startTimestamp=Time return results -def get_performance_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), +def get_performance_avg_image_load_time(cur, 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) @@ -2286,34 +2286,33 @@ def get_performance_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(d 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_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) + 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 @@ -2341,7 +2340,7 @@ def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeU with pg_client.PostgresClient() as cur: row = __get_application_activity_avg_page_load_time(cur, project_id, startTimestamp, endTimestamp, **args) results = row - results["chart"] = get_performance_avg_page_load_time(project_id, startTimestamp, endTimestamp, **args) + results["chart"] = get_performance_avg_page_load_time(cur, project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2352,7 +2351,7 @@ def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeU return results -def get_performance_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), +def get_performance_avg_page_load_time(cur, 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) @@ -2360,31 +2359,30 @@ def get_performance_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(de 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() + 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 @@ -2411,7 +2409,7 @@ def get_application_activity_avg_request_load_time(project_id, startTimestamp=Ti with pg_client.PostgresClient() as cur: row = __get_application_activity_avg_request_load_time(cur, project_id, startTimestamp, endTimestamp, **args) results = row - results["chart"] = get_performance_avg_request_load_time(project_id, startTimestamp, endTimestamp, **args) + results["chart"] = get_performance_avg_request_load_time(cur, project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2422,7 +2420,7 @@ def get_application_activity_avg_request_load_time(project_id, startTimestamp=Ti return results -def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), +def get_performance_avg_request_load_time(cur, 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) @@ -2431,33 +2429,33 @@ def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now 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() + 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/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 9e56cc351..7e85c3169 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2097,7 +2097,7 @@ def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeU 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) + results["chart"] = get_performance_avg_page_load_time(ch, project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2126,7 +2126,7 @@ def __get_application_activity_avg_page_load_time(ch, project_id, startTimestamp return result -def get_performance_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), +def get_performance_avg_page_load_time(ch, 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) @@ -2143,30 +2143,30 @@ def get_performance_avg_page_load_time(project_id, startTimestamp=TimeUTC.now(de 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;""" + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, + data=args) + ch_sub_query_chart += meta_condition - 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})] + 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;""" - for s in pages: - for k in s: - if s[k] is None: - s[k] = 0 + 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 @@ -2175,7 +2175,7 @@ def get_application_activity_avg_image_load_time(project_id, startTimestamp=Time 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) + results["chart"] = get_performance_avg_image_load_time(ch, project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2204,7 +2204,7 @@ def __get_application_activity_avg_image_load_time(ch, project_id, startTimestam return result -def get_performance_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), +def get_performance_avg_image_load_time(ch, 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) @@ -2223,25 +2223,24 @@ def get_performance_avg_image_load_time(project_id, startTimestamp=TimeUTC.now(d 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})] + 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 + for s in images: + for k in s: + if s[k] is None: + s[k] = 0 return images @@ -2250,7 +2249,7 @@ def get_application_activity_avg_request_load_time(project_id, startTimestamp=Ti 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) + results["chart"] = get_performance_avg_request_load_time(ch, project_id, startTimestamp, endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2279,7 +2278,7 @@ def __get_application_activity_avg_request_load_time(ch, project_id, startTimest return result -def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), +def get_performance_avg_request_load_time(ch, 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) @@ -2297,26 +2296,26 @@ def get_performance_avg_request_load_time(project_id, startTimestamp=TimeUTC.now 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 + 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 From ae93db4a7e5957f3887106f2b391011b89e6d2e6 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 13:56:54 +0200 Subject: [PATCH 130/294] feat(db): requests structure change --- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 299 +++++++++++++++--- .../db/init_dbs/postgresql/init_schema.sql | 288 ++++++++++++++--- .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 15 +- .../db/init_dbs/postgresql/init_schema.sql | 288 ++++++++++++++--- 4 files changed, 744 insertions(+), 146 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 8cafd767d..3867fb5df 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,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 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 default_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) ), @@ -48,6 +52,11 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets config jsonb NOT NULL DEFAULT '{}'::jsonb ); +ALTER TABLE events_common.requests + ADD COLUMN IF NOT EXISTS host text NULL, + ADD COLUMN IF NOT EXISTS base_path text NULL, + ADD COLUMN IF NOT EXISTS query text NULL; + COMMIT; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'barChart'; @@ -58,55 +67,236 @@ 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, 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'), - ('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'), +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'), + ('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'), + ('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'), + ('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":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'), - ('Breakdown of Loaded Resources', 'resources', '{"col":2,"row":2,"position":0}', true, true, true, 'resources_count_by_type', 'predefined', 'stackedBarChart') + ('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'), + ('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, @@ -115,4 +305,11 @@ ON CONFLICT (predefined_key) DO UPDATE 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 + view_type=excluded.view_type; + +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_idx ON events_common.requests (host) WHERE host IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_gin_idx ON events_common.requests USING GIN (host gin_trgm_ops) WHERE host IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; \ 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 5abd6f026..b83f2f84d 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,11 @@ $$ is_predefined boolean NOT NULL DEFAULT FALSE, is_template boolean NOT NULL DEFAULT FALSE, predefined_key text NULL DEFAULT NULL, - default_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) @@ -1229,6 +1233,9 @@ $$ response_body text NULL, status_code smallint NULL, method http_method NULL, + host text NULL, + base_path text NULL, + query text NULL, PRIMARY KEY (session_id, timestamp, seq_index) ); CREATE INDEX IF NOT EXISTS requests_url_idx ON events_common.requests (url); @@ -1250,6 +1257,12 @@ $$ CREATE INDEX IF NOT EXISTS requests_response_body_nn_idx ON events_common.requests (response_body) WHERE response_body IS NOT NULL; CREATE INDEX IF NOT EXISTS requests_response_body_nn_gin_idx ON events_common.requests USING GIN (response_body gin_trgm_ops) WHERE response_body IS NOT NULL; CREATE INDEX IF NOT EXISTS requests_status_code_nn_idx ON events_common.requests (status_code) WHERE status_code IS NOT NULL; + CREATE INDEX IF NOT EXISTS requests_host_nn_idx ON events_common.requests (host) WHERE host IS NOT NULL; + CREATE INDEX IF NOT EXISTS requests_host_nn_gin_idx ON events_common.requests USING GIN (host gin_trgm_ops) WHERE host IS NOT NULL; + CREATE INDEX IF NOT EXISTS requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; + CREATE INDEX IF NOT EXISTS requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; + CREATE INDEX IF NOT EXISTS requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; + CREATE INDEX IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; END IF; @@ -1257,55 +1270,236 @@ $$ $$ LANGUAGE plpgsql; -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'), - ('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'), +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'), + ('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'), + ('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'), + ('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":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') + ('Missing Resources', 'resources', '{ + "col": 2, + "row": 2, + "position": 0 + }', true, true, true, 'missing_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, 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 c50f6fc42..f639f3973 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,6 +48,11 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets config jsonb NOT NULL DEFAULT '{}'::jsonb ); +ALTER TABLE events_common.requests + ADD COLUMN IF NOT EXISTS host text NULL, + ADD COLUMN IF NOT EXISTS base_path text NULL, + ADD COLUMN IF NOT EXISTS query text NULL; + COMMIT; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'barChart'; @@ -114,4 +119,12 @@ ON CONFLICT (predefined_key) DO UPDATE 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 + view_type=excluded.view_type; + + +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_idx ON events_common.requests (host) WHERE host IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_gin_idx ON events_common.requests USING GIN (host gin_trgm_ops) WHERE host IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index d78d0754b..1a77e2b7b 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -645,6 +645,9 @@ $$ response_body text NULL, status_code smallint NULL, method http_method NULL, + host text NULL, + base_path text NULL, + query text NULL, PRIMARY KEY (session_id, timestamp, seq_index) ); CREATE INDEX requests_url_idx ON events_common.requests (url); @@ -664,6 +667,12 @@ $$ CREATE INDEX requests_response_body_nn_idx ON events_common.requests (response_body) WHERE response_body IS NOT NULL; CREATE INDEX requests_response_body_nn_gin_idx ON events_common.requests USING GIN (response_body gin_trgm_ops) WHERE response_body IS NOT NULL; CREATE INDEX requests_status_code_nn_idx ON events_common.requests (status_code) WHERE status_code IS NOT NULL; + CREATE INDEX requests_host_nn_idx ON events_common.requests (host) WHERE host IS NOT NULL; + CREATE INDEX requests_host_nn_gin_idx ON events_common.requests USING GIN (host gin_trgm_ops) WHERE host IS NOT NULL; + CREATE INDEX requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; + CREATE INDEX requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; + CREATE INDEX requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; + CREATE INDEX requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; -- --- events.sql --- CREATE SCHEMA IF NOT EXISTS events; @@ -958,7 +967,11 @@ $$ is_predefined boolean NOT NULL DEFAULT FALSE, is_template boolean NOT NULL DEFAULT FALSE, predefined_key text NULL DEFAULT NULL, - default_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,55 +1061,236 @@ $$ $$ LANGUAGE plpgsql; -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'), - ('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'), +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'), + ('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'), + ('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'), + ('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":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') + ('Missing Resources', 'resources', '{ + "col": 2, + "row": 2, + "position": 0 + }', true, true, true, 'missing_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, From 3bc9bd8df4fbdfad58aca0ef9a6ffe5a285be052 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Wed, 13 Apr 2022 14:26:39 +0200 Subject: [PATCH 131/294] feat(backend): insert url parts for pages and requests --- backend/pkg/db/postgres/messages-web-stats.go | 2 +- backend/pkg/db/postgres/messages-web.go | 44 ++++++++++++------- backend/pkg/url/url.go | 13 +++--- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/backend/pkg/db/postgres/messages-web-stats.go b/backend/pkg/db/postgres/messages-web-stats.go index 933442b0b..27a6272e2 100644 --- a/backend/pkg/db/postgres/messages-web-stats.go +++ b/backend/pkg/db/postgres/messages-web-stats.go @@ -35,7 +35,7 @@ func (conn *Conn) InsertWebStatsPerformance(sessionID uint64, p *PerformanceTrac } func (conn *Conn) InsertWebStatsResourceEvent(sessionID uint64, e *ResourceEvent) error { - host, _, err := url.GetURLParts(e.URL) + host, _, _, err := url.GetURLParts(e.URL) if err != nil { return err } diff --git a/backend/pkg/db/postgres/messages-web.go b/backend/pkg/db/postgres/messages-web.go index 0d822cddd..ff7352594 100644 --- a/backend/pkg/db/postgres/messages-web.go +++ b/backend/pkg/db/postgres/messages-web.go @@ -55,7 +55,7 @@ func (conn *Conn) InsertWebUserAnonymousID(sessionID uint64, userAnonymousID *Us // TODO: fix column "dom_content_loaded_event_end" of relation "pages" func (conn *Conn) InsertWebPageEvent(sessionID uint64, e *PageEvent) error { - host, path, err := url.GetURLParts(e.URL) + host, path, query, err := url.GetURLParts(e.URL) if err != nil { return err } @@ -64,20 +64,27 @@ func (conn *Conn) InsertWebPageEvent(sessionID uint64, e *PageEvent) error { return err } defer tx.rollback() + // base_path is depricated if err := tx.exec(` INSERT INTO events.pages ( - session_id, message_id, timestamp, referrer, base_referrer, host, path, base_path, + session_id, message_id, timestamp, referrer, base_referrer, host, path, query, dom_content_loaded_time, load_time, response_end, first_paint_time, first_contentful_paint_time, speed_index, visually_complete, time_to_interactive, - response_time, dom_building_time + response_time, dom_building_time, + base_path ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, + $1, $2, $3, + $4, $5, + $6, $7, $8, NULLIF($9, 0), NULLIF($10, 0), NULLIF($11, 0), NULLIF($12, 0), NULLIF($13, 0), NULLIF($14, 0), NULLIF($15, 0), NULLIF($16, 0), - NULLIF($17, 0), NULLIF($18, 0) + NULLIF($17, 0), NULLIF($18, 0), + '', ) `, - sessionID, e.MessageID, e.Timestamp, e.Referrer, url.DiscardURLQuery(e.Referrer), host, path, url.DiscardURLQuery(path), + sessionID, e.MessageID, e.Timestamp, + e.Referrer, url.DiscardURLQuery(e.Referrer), + host, path, query, e.DomContentLoadedEventEnd, e.LoadEventEnd, e.ResponseEnd, e.FirstPaint, e.FirstContentfulPaint, e.SpeedIndex, e.VisuallyComplete, e.TimeToInteractive, calcResponseTime(e), calcDomBuildingTime(e), @@ -109,7 +116,7 @@ func (conn *Conn) InsertWebClickEvent(sessionID uint64, e *ClickEvent) error { INSERT INTO events.clicks (session_id, message_id, timestamp, label, selector, url) (SELECT - $1, $2, $3, NULLIF($4, ''), $5, host || base_path + $1, $2, $3, NULLIF($4, ''), $5, host || path FROM events.pages WHERE session_id = $1 AND timestamp <= $3 ORDER BY timestamp DESC LIMIT 1 ) @@ -211,19 +218,26 @@ func (conn *Conn) InsertWebFetchEvent(sessionID uint64, savePayload bool, e *Fet response = &e.Response } conn.insertAutocompleteValue(sessionID, "REQUEST", url.DiscardURLQuery(e.URL)) + host, path, query, err := url.GetURLParts(e.URL) + if err != nil { + return err + } return conn.batchQueue(sessionID, ` INSERT INTO events_common.requests ( - session_id, timestamp, - seq_index, url, duration, success, - request_body, response_body, status_code, method + session_id, timestamp, seq_index, + url, host, path, query, + request_body, response_body, status_code, method, + duration, success, ) VALUES ( - $1, $2, - $3, $4, $5, $6, - $7, $8, $9::smallint, NULLIF($10, '')::http_method + $1, $2, $3, + $4, $5, $6, $7 + $8, $9, $10::smallint, NULLIF($11, '')::http_method, + $12, $13 ) ON CONFLICT DO NOTHING`, - sessionID, e.Timestamp, - getSqIdx(e.MessageID), e.URL, e.Duration, e.Status < 400, + sessionID, e.Timestamp, getSqIdx(e.MessageID), + e.URL, host, path, query, request, response, e.Status, url.EnsureMethod(e.Method), + e.Duration, e.Status < 400, ) } diff --git a/backend/pkg/url/url.go b/backend/pkg/url/url.go index b9181774d..48cd0ef8d 100644 --- a/backend/pkg/url/url.go +++ b/backend/pkg/url/url.go @@ -1,18 +1,19 @@ package url import ( - "strings" _url "net/url" + "strings" ) func DiscardURLQuery(url string) string { return strings.Split(url, "?")[0] -} +} -func GetURLParts(rawURL string) (string, string, error) { +func GetURLParts(rawURL string) (string, string, string, error) { u, err := _url.Parse(rawURL) if err != nil { - return "", "", err + return "", "", "", err } - return u.Host, u.RequestURI(), nil -} \ No newline at end of file + // u.Scheme ? + return u.Host, u.RawPath, u.RawQuery, nil +} From abde6b62a85d7624bee8dbd70b175cb7a4c4fd37 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 14:44:35 +0200 Subject: [PATCH 132/294] feat(db): pages structure change --- ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 8 +++++++- ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql | 4 ++++ scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 6 ++++++ scripts/helm/db/init_dbs/postgresql/init_schema.sql | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) 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 3867fb5df..ba00ea335 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 @@ -57,7 +57,10 @@ ALTER TABLE events_common.requests ADD COLUMN IF NOT EXISTS base_path text NULL, ADD COLUMN IF NOT EXISTS query text NULL; +ALTER TABLE events.pages + ADD COLUMN IF NOT EXISTS query text NULL; COMMIT; + ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'barChart'; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'stackedBarChart'; @@ -312,4 +315,7 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_gin_idx ON events_commo CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; -CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; \ No newline at end of file +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; + +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; \ 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 b83f2f84d..bc8d65f93 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -939,6 +939,7 @@ $$ host text NOT NULL, path text NOT NULL, base_path text NOT NULL, + query text NULL, referrer text DEFAULT NULL, base_referrer text DEFAULT NULL, dom_building_time integer DEFAULT NULL, @@ -998,6 +999,9 @@ $$ 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); CREATE INDEX IF NOT EXISTS pages_base_path_base_pathLNGT2_idx ON events.pages (base_path) WHERE length(base_path) > 2; + CREATE INDEX IF NOT EXISTS pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; + CREATE INDEX IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; + CREATE TABLE IF NOT EXISTS events.clicks ( 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 f639f3973..9e6421b07 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,7 +53,10 @@ ALTER TABLE events_common.requests ADD COLUMN IF NOT EXISTS base_path text NULL, ADD COLUMN IF NOT EXISTS query text NULL; +ALTER TABLE events.pages + ADD COLUMN IF NOT EXISTS query text NULL; COMMIT; + ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'barChart'; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'stackedBarChart'; @@ -128,3 +131,6 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_idx ON events_comm CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; + +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 1a77e2b7b..7f4d864fe 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -685,6 +685,7 @@ $$ host text NOT NULL, path text NOT NULL, base_path text NOT NULL, + query text NULL, referrer text DEFAULT NULL, base_referrer text DEFAULT NULL, dom_building_time integer DEFAULT NULL, @@ -740,6 +741,8 @@ $$ CREATE INDEX 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 pages_base_path_session_id_timestamp_idx ON events.pages (base_path, session_id, timestamp); CREATE INDEX pages_base_path_base_pathLNGT2_idx ON events.pages (base_path) WHERE length(base_path) > 2; + CREATE INDEX IF NOT EXISTS pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; + CREATE INDEX IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; CREATE TABLE events.clicks From 0fcaf7bce001fc47f1fa60acefa404f4eddb5ed1 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Wed, 13 Apr 2022 15:56:24 +0200 Subject: [PATCH 133/294] feat(backend): request autocomplete use path --- backend/pkg/db/postgres/messages-web.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkg/db/postgres/messages-web.go b/backend/pkg/db/postgres/messages-web.go index ff7352594..eb4bca364 100644 --- a/backend/pkg/db/postgres/messages-web.go +++ b/backend/pkg/db/postgres/messages-web.go @@ -217,8 +217,8 @@ func (conn *Conn) InsertWebFetchEvent(sessionID uint64, savePayload bool, e *Fet request = &e.Request response = &e.Response } - conn.insertAutocompleteValue(sessionID, "REQUEST", url.DiscardURLQuery(e.URL)) host, path, query, err := url.GetURLParts(e.URL) + conn.insertAutocompleteValue(sessionID, "REQUEST", path) if err != nil { return err } From cbec34db3e6b5dada4793c1490528291f63272ba Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 16:23:31 +0200 Subject: [PATCH 134/294] feat(api): dashboard added charts for avg_dom_content_loaded widgets --- api/chalicelib/core/dashboard.py | 30 +++++++++++++++++++++----- ee/api/chalicelib/core/dashboard.py | 33 ++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 1fb526d96..66ee770d8 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2672,11 +2672,15 @@ def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None, **args): + endTimestamp=TimeUTC.now(), value=None, density=19, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, + chart=True, data=args) if value is not None: pg_sub_query.append("pages.path = %(value)s") + pg_sub_query_chart.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 @@ -2685,11 +2689,27 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no 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)})) + params = {"step_size": step_size, + "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + + pg_query = f"""SELECT generated_timestamp AS timestamp, + COALESCE(AVG(NULLIF(pages.dom_content_loaded_time,0)),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT dom_content_loaded_time + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp ASC;""" + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + row["chart"] = helper.list_to_camel_case(rows), row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 7e85c3169..dc99ac0d2 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2552,23 +2552,40 @@ def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta 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) + endTimestamp=TimeUTC.now(), value=None, density=19, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density) + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) ch_sub_query += meta_condition if value is not None: ch_sub_query.append("pages.url_path = %(value)s") + ch_sub_query_chart.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]) + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + rows = ch.execute(query=ch_query, params=params) + results = helper.dict_to_camel_case(rows[0]) + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, + COALESCE(AVG(NULLIF(pages.dom_content_loaded_event_time ,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_chart)} + GROUP BY timestamp + ORDER BY timestamp;;""" + rows = ch.execute(query=ch_query, params=params) + results["chart"] = helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, + neutral={"value": 0})) + return results def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(delta_days=-1), From bb6401b675a11162d58fa49084b9129b21f524c9 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 13 Apr 2022 16:37:21 +0200 Subject: [PATCH 135/294] change(ui) - remove projects from client, and other fixes --- frontend/app/Router.js | 45 +++++------- frontend/app/api_client.js | 2 +- .../components/Announcements/Announcements.js | 2 +- .../app/components/BugFinder/BugFinder.js | 2 +- .../SessionCaptureRate/SessionCaptureRate.js | 2 +- .../Client/CustomFields/CustomFields.js | 2 +- .../Client/Integrations/IntegrationForm.js | 4 +- .../components/Client/Sites/NewSiteForm.js | 3 +- .../components/Client/Webhooks/Webhooks.js | 2 +- frontend/app/components/Client/client.css | 2 +- .../DashboardHeader/DashboardHeader.js | 2 +- .../app/components/Dashboard/NewDashboard.tsx | 18 ++--- .../Dashboard/SideMenu/SideMenuSection.js | 2 +- .../CustomMetriLineChart.tsx | 29 ++++---- .../CustomMetricOverviewChart.tsx | 18 ++--- .../CustomMetricPercentage.tsx | 2 +- .../CustomMetricWidget/CustomMetricWidget.tsx | 2 +- .../CustomMetricWidgetPreview.tsx | 2 +- .../PredefinedWidgets/CPULoad/CPULoad.tsx | 2 +- .../PredefinedWidgets/Crashes/Crashes.tsx | 5 +- .../DomBuildingTime/DomBuildingTime.tsx | 6 +- .../Widgets/PredefinedWidgets/FPS/FPS.tsx | 4 +- .../MemoryConsumption/MemoryConsumption.tsx | 2 +- .../DashboardView/DashboardView.tsx | 29 ++++---- .../components/WidgetChart/WidgetChart.tsx | 44 +++++++++--- .../WidgetSessions/WidgetSessions.tsx | 8 +-- .../components/WidgetView/WidgetView.tsx | 2 +- .../WidgetWrapper/WidgetWrapper.tsx | 1 + .../Funnels/FunnelDetails/FunnelDetails.js | 2 +- .../Funnels/FunnelHeader/FunnelDropdown.js | 2 +- .../FunnelIssueDetails/FunnelIssueDetails.js | 2 +- .../Funnels/FunnelIssues/FunnelIssues.js | 2 +- .../Funnels/FunnelList/FunnelList.js | 2 +- frontend/app/components/Header/Header.js | 2 +- .../OnboardingExplore/OnboardingExplore.js | 2 +- .../app/components/Header/SiteDropdown.js | 4 +- .../Session/Layout/ToolPanel/StackEvents.js | 2 +- .../Session_/Player/Overlay/AutoplayTimer.tsx | 2 +- .../components/Session_/PlayerBlockHeader.js | 2 +- .../Session_/StackEvents/StackEvents.js | 10 ++- .../StackEvents/UserEvent/UserEvent.js | 72 ++++++++++--------- .../app/components/hocs/withSiteIdRouter.js | 4 +- .../app/components/hocs/withSiteIdUpdater.js | 4 +- .../shared/SessionItem/SessionItem.js | 2 +- .../shared/SiteDropdown/SiteDropdown.js | 4 +- frontend/app/components/ui/Link/Link.js | 2 +- .../ui/SavedSearchList/SavedSearchList.js | 2 +- frontend/app/duck/site.js | 39 ++++++++-- frontend/app/duck/user.js | 29 ++------ frontend/app/mstore/dashboardStore.ts | 14 +++- frontend/app/mstore/types/filter.ts | 17 +++-- frontend/app/types/client/client.js | 10 +-- 52 files changed, 265 insertions(+), 212 deletions(-) diff --git a/frontend/app/Router.js b/frontend/app/Router.js index f42f3c456..ab41173bd 100644 --- a/frontend/app/Router.js +++ b/frontend/app/Router.js @@ -22,7 +22,7 @@ const FunnelIssueDetails = lazy(() => import('Components/Funnels/FunnelIssueDeta import WidgetViewPure from 'Components/Dashboard/components/WidgetView'; import Header from 'Components/Header/Header'; // import ResultsModal from 'Shared/Results/ResultsModal'; -import { fetchList as fetchIntegrationVariables } from 'Duck/customField'; +import { fetchList as fetchMetadata } from 'Duck/customField'; import { fetchList as fetchSiteList } from 'Duck/site'; import { fetchList as fetchAnnouncements } from 'Duck/announcements'; import { fetchList as fetchAlerts } from 'Duck/alerts'; @@ -80,7 +80,7 @@ const ONBOARDING_REDIRECT_PATH = routes.onboarding(OB_DEFAULT_TAB); @withStore @withRouter @connect((state) => { - const siteId = state.getIn([ 'user', 'siteId' ]); + const siteId = state.getIn([ 'site', 'siteId' ]); const jwt = state.get('jwt'); const changePassword = state.getIn([ 'user', 'account', 'changePassword' ]); const userInfoLoading = state.getIn([ 'user', 'fetchUserInfoRequest', 'loading' ]); @@ -88,7 +88,7 @@ const ONBOARDING_REDIRECT_PATH = routes.onboarding(OB_DEFAULT_TAB); jwt, siteId, changePassword, - sites: state.getIn([ 'user', 'client', 'sites' ]), + sites: state.getIn([ 'site', 'list' ]), isLoggedIn: jwt !== null && !changePassword, loading: siteId === null || userInfoLoading, email: state.getIn([ 'user', 'account', 'email' ]), @@ -103,7 +103,7 @@ const ONBOARDING_REDIRECT_PATH = routes.onboarding(OB_DEFAULT_TAB); fetchUserInfo, fetchTenants, setSessionPath, - fetchIntegrationVariables, + fetchMetadata, fetchSiteList, fetchAnnouncements, fetchAlerts, @@ -124,17 +124,18 @@ class Router extends React.Component { fetchInitialData = () => { Promise.all([ this.props.fetchUserInfo().then(() => { - const { mstore } = this.props - mstore.initClient(); - this.props.fetchIntegrationVariables() - }), - this.props.fetchSiteList().then(() => { - setTimeout(() => { - this.props.fetchAnnouncements(); - this.props.fetchAlerts(); - this.props.fetchWatchdogStatus(); - }, 100); - }), + this.props.fetchSiteList().then(() => { + const { mstore } = this.props + mstore.initClient(); + + setTimeout(() => { + this.props.fetchMetadata() + this.props.fetchAnnouncements(); + this.props.fetchAlerts(); + this.props.fetchWatchdogStatus(); + }, 100); + }) + }) ]) } @@ -197,25 +198,17 @@ class Router extends React.Component { { onboarding && } - { siteIdList.length === 0 && + {/* { siteIdList.length === 0 && - } + } */} + {/* DASHBOARD and Metrics */} - - - - - {/* - - - - */} diff --git a/frontend/app/api_client.js b/frontend/app/api_client.js index 626f033ea..98a1f4dfd 100644 --- a/frontend/app/api_client.js +++ b/frontend/app/api_client.js @@ -56,7 +56,7 @@ export const clean = (obj, forbidenValues = [ undefined, '' ]) => { export default class APIClient { constructor() { const jwt = store.getState().get('jwt'); - const siteId = store.getState().getIn([ 'user', 'siteId' ]); + const siteId = store.getState().getIn([ 'site', 'siteId' ]); this.init = { headers: { Accept: 'application/json', diff --git a/frontend/app/components/Announcements/Announcements.js b/frontend/app/components/Announcements/Announcements.js index e0d09f024..6252e4d79 100644 --- a/frontend/app/components/Announcements/Announcements.js +++ b/frontend/app/components/Announcements/Announcements.js @@ -96,6 +96,6 @@ class Announcements extends React.Component { export default connect(state => ({ announcements: state.getIn(['announcements', 'list']), loading: state.getIn(['announcements', 'fetchList', 'loading']), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), sites: state.getIn([ 'site', 'list' ]), }), { fetchList, setLastRead })(Announcements); \ No newline at end of file diff --git a/frontend/app/components/BugFinder/BugFinder.js b/frontend/app/components/BugFinder/BugFinder.js index 85f899290..3ce09d23b 100644 --- a/frontend/app/components/BugFinder/BugFinder.js +++ b/frontend/app/components/BugFinder/BugFinder.js @@ -53,7 +53,7 @@ const allowedQueryKeys = [ sources: state.getIn([ 'customFields', 'sources' ]), filterValues: state.get('filterValues'), favoriteList: state.getIn([ 'sessions', 'favoriteList' ]), - currentProjectId: state.getIn([ 'user', 'siteId' ]), + currentProjectId: state.getIn([ 'site', 'siteId' ]), sites: state.getIn([ 'site', 'list' ]), watchdogs: state.getIn(['watchdogs', 'list']), activeFlow: state.getIn([ 'filters', 'activeFlow' ]), diff --git a/frontend/app/components/BugFinder/SessionCaptureRate/SessionCaptureRate.js b/frontend/app/components/BugFinder/SessionCaptureRate/SessionCaptureRate.js index 4c2b41218..f545bcacd 100644 --- a/frontend/app/components/BugFinder/SessionCaptureRate/SessionCaptureRate.js +++ b/frontend/app/components/BugFinder/SessionCaptureRate/SessionCaptureRate.js @@ -72,7 +72,7 @@ const SessionCaptureRate = props => { } export default connect(state => ({ - currentProjectId: state.getIn([ 'user', 'siteId' ]), + currentProjectId: state.getIn([ 'site', 'siteId' ]), captureRate: state.getIn(['watchdogs', 'captureRate']), loading: state.getIn(['watchdogs', 'savingCaptureRate', 'loading']), }), { diff --git a/frontend/app/components/Client/CustomFields/CustomFields.js b/frontend/app/components/Client/CustomFields/CustomFields.js index 081f11a58..b46994f0b 100644 --- a/frontend/app/components/Client/CustomFields/CustomFields.js +++ b/frontend/app/components/Client/CustomFields/CustomFields.js @@ -13,7 +13,7 @@ import { confirm } from 'UI/Confirmation'; fields: state.getIn(['customFields', 'list']).sortBy(i => i.index), field: state.getIn(['customFields', 'instance']), loading: state.getIn(['customFields', 'fetchRequest', 'loading']), - sites: state.getIn([ 'user', 'client', 'sites' ]), + sites: state.getIn([ 'site', 'list' ]), errors: state.getIn([ 'customFields', 'saveRequest', 'errors' ]), }), { init, diff --git a/frontend/app/components/Client/Integrations/IntegrationForm.js b/frontend/app/components/Client/Integrations/IntegrationForm.js index 3481068de..239958233 100644 --- a/frontend/app/components/Client/Integrations/IntegrationForm.js +++ b/frontend/app/components/Client/Integrations/IntegrationForm.js @@ -4,8 +4,8 @@ import SiteDropdown from 'Shared/SiteDropdown'; import { save, init, edit, remove, fetchList } from 'Duck/integrations/actions'; @connect((state, { name, customPath }) => ({ - sites: state.getIn([ 'user', 'client', 'sites' ]), - initialSiteId: state.getIn([ 'user', 'siteId' ]), + sites: state.getIn([ 'site', 'list' ]), + initialSiteId: state.getIn([ 'site', 'siteId' ]), list: state.getIn([ name, 'list' ]), config: state.getIn([ name, 'instance']), saving: state.getIn([ customPath || name, 'saveRequest', 'loading']), diff --git a/frontend/app/components/Client/Sites/NewSiteForm.js b/frontend/app/components/Client/Sites/NewSiteForm.js index 011ed8b01..b98a77a38 100644 --- a/frontend/app/components/Client/Sites/NewSiteForm.js +++ b/frontend/app/components/Client/Sites/NewSiteForm.js @@ -1,7 +1,8 @@ import { connect } from 'react-redux'; import { Input, Button, Label } from 'UI'; import { save, edit, update , fetchList } from 'Duck/site'; -import { pushNewSite, setSiteId } from 'Duck/user'; +import { pushNewSite } from 'Duck/user'; +import { setSiteId } from 'Duck/site'; import { withRouter } from 'react-router-dom'; import styles from './siteForm.css'; diff --git a/frontend/app/components/Client/Webhooks/Webhooks.js b/frontend/app/components/Client/Webhooks/Webhooks.js index 100887285..150ed05a6 100644 --- a/frontend/app/components/Client/Webhooks/Webhooks.js +++ b/frontend/app/components/Client/Webhooks/Webhooks.js @@ -59,7 +59,7 @@ class Webhooks extends React.PureComponent { title="No webhooks available." size="small" show={ noSlackWebhooks.size === 0 } - icon + animatedIcon="no-results" >
{ noSlackWebhooks.map(webhook => ( diff --git a/frontend/app/components/Client/client.css b/frontend/app/components/Client/client.css index d9406e4d8..8e69458ef 100644 --- a/frontend/app/components/Client/client.css +++ b/frontend/app/components/Client/client.css @@ -18,7 +18,7 @@ & .tabContent { background-color: white; padding: 25px; - margin-top: -30px; + /* margin-top: -30px; */ margin-right: -20px; width: 100%; } diff --git a/frontend/app/components/Dashboard/DashboardHeader/DashboardHeader.js b/frontend/app/components/Dashboard/DashboardHeader/DashboardHeader.js index dd2c35c76..c4160b598 100644 --- a/frontend/app/components/Dashboard/DashboardHeader/DashboardHeader.js +++ b/frontend/app/components/Dashboard/DashboardHeader/DashboardHeader.js @@ -35,6 +35,6 @@ const DashboardHeader = props => { export default connect(state => ({ period: state.getIn([ 'dashboard', 'period' ]), platform: state.getIn([ 'dashboard', 'platform' ]), - currentProjectId: state.getIn([ 'user', 'siteId' ]), + currentProjectId: state.getIn([ 'site', 'siteId' ]), sites: state.getIn([ 'site', 'list' ]), }), { setPeriod, setPlatform })(DashboardHeader) diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index ca62377f3..6b77ff830 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -3,18 +3,16 @@ import withPageTitle from 'HOCs/withPageTitle'; import { observer, useObserver } from "mobx-react-lite"; import { useStore } from 'App/mstore'; import { withRouter } from 'react-router-dom'; -import { - dashboardSelected, - withSiteId, -} from 'App/routes'; import DashboardSideMenu from './components/DashboardSideMenu'; import { Loader } from 'UI'; import DashboardRouter from './components/DashboardRouter'; +import cn from 'classnames'; function NewDashboard(props) { const { history, match: { params: { siteId, dashboardId, metricId } } } = props; const { dashboardStore } = useStore(); const loading = useObserver(() => dashboardStore.isLoading); + const isMetricDetails = history.location.pathname.includes('/metrics/') || history.location.pathname.includes('/metric/'); useEffect(() => { dashboardStore.fetchList().then((resp) => { @@ -32,20 +30,18 @@ function NewDashboard(props) { }); }, [siteId]); - return ( + return useObserver(() => (
-
+
-
+
- ); + )); } -export default withPageTitle('New Dashboard')( - withRouter(observer(NewDashboard)) -); \ No newline at end of file +export default withPageTitle('New Dashboard')(withRouter(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 801c02415..494ce7128 100644 --- a/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js +++ b/frontend/app/components/Dashboard/SideMenu/SideMenuSection.js @@ -41,5 +41,5 @@ function SideMenuSection({ title, items, onItemClick, setShowAlerts, siteId }) { SideMenuSection.displayName = "SideMenuSection"; export default connect(state => ({ - siteId: state.getIn([ 'user', 'siteId' ]) + siteId: state.getIn([ 'site', 'siteId' ]) }), { setShowAlerts })(SideMenuSection); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx index 59417bd49..957f44deb 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx @@ -6,16 +6,17 @@ import { LineChart, Line, Legend } from 'recharts'; interface Props { data: any; params: any; - seriesMap: any; + // seriesMap: any; colors: any; onClick?: (event, index) => void; } function CustomMetriLineChart(props: Props) { - const { data, params, seriesMap = [], colors, onClick = () => null } = props; + const { data = { chart: [], namesMap: [] }, params, colors, onClick = () => null } = props; + return ( - { seriesMap.map((key, index) => ( + { Array.isArray(data.namesMap) && data.namesMap.map((key, index) => ( ))} diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx index 769e63fb4..1f65e1c81 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart/CustomMetricOverviewChart.tsx @@ -16,24 +16,24 @@ function CustomMetricOverviewChart(props: Props) { return (
-
+
- +
{gradientDef} diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx index 0dfa6dbd3..7e3681f45 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx @@ -12,7 +12,7 @@ function CustomMetriPercentage(props: Props) { return (
{numberWithCommas(data.count)}
-
{`${parseInt(data.previousCount).toFixed(1)} ( ${parseInt(data.countProgress).toFixed(1)}% )`}
+
{`${parseInt(data.previousCount)} ( ${parseInt(data.countProgress).toFixed(1)}% )`}
from previous period.
) diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx index 32f800e1f..15acd21bb 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidget/CustomMetricWidget.tsx @@ -136,7 +136,7 @@ function CustomMetricWidget(props: Props) { diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx index 73321405c..a1a2534f9 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricWidgetPreview/CustomMetricWidgetPreview.tsx @@ -168,7 +168,7 @@ function CustomMetricWidget(props: Props) { { metric.viewType === 'lineChart' && ( diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx index 5e5853251..d64747585 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx @@ -40,7 +40,7 @@ function CPULoad(props: Props) { name="Avg" type="monotone" unit="%" - dataKey="avgCpu" + dataKey="value" stroke={Styles.colors[0]} fillOpacity={ 1 } strokeWidth={ 2 } diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx index a73537d69..5dffda63d 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx @@ -32,14 +32,13 @@ function Crashes(props: Props) { {...Styles.yaxis} allowDecimals={false} tickFormatter={val => Styles.tickFormatter(val)} - label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + label={{ ...Styles.axisLabelLeft, value: "Number of Crashes" }} /> Styles.tickFormatter(val)} - label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + label={{ ...Styles.axisLabelLeft, value: "DOM Build Time (ms)" }} /> Styles.tickFormatter(val)} - label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + label={{ ...Styles.axisLabelLeft, value: "Frames Per Second" }} />
- Time Range + {/* Time Range */}
- +
+ More + +
void; } function WidgetChart(props: Props) { const { isWidget = false, metric } = props; @@ -19,12 +21,32 @@ function WidgetChart(props: Props) { const period = useObserver(() => dashboardStore.period); const colors = Styles.customMetricColors; const [loading, setLoading] = useState(false) - const [seriesMap, setSeriesMap] = useState([]); - const params = { density: 28 } + const isOverviewWidget = metric.metricType === 'predefined' && metric.viewType === 'overview'; + const params = { density: isOverviewWidget ? 7 : 70 } const metricParams = { ...params } const prevMetricRef = useRef(); const [data, setData] = useState(metric.data); + const onChartClick = (event: any) => { + if (event) { + const payload = event.activePayload[0].payload; + const timestamp = payload.timestamp; + const periodTimestamps = metric.metricType === 'timeseries' ? + getStartAndEndTimestampsByDensity(timestamp, period.start, period.end, params.density) : + period.toTimestamps(); + + // const activeWidget = { + // widget: metric, + // period: period, + // ...periodTimestamps, + // timestamp: payload.timestamp, + // index, + // } + + // props.setActiveWidget(activeWidget); + } + } + useEffect(() => { if (prevMetricRef.current && prevMetricRef.current.name !== metric.name) { prevMetricRef.current = metric; @@ -33,8 +55,8 @@ function WidgetChart(props: Props) { prevMetricRef.current = metric; setLoading(true); - const data = isWidget ? { ...params } : { ...metricParams, ...metric.toJson() }; - dashboardStore.fetchMetricChartData(metric, data, isWidget).then((res: any) => { + const payload = isWidget ? { ...params } : { ...metricParams, ...metric.toJson() }; + dashboardStore.fetchMetricChartData(metric, payload, isWidget).then((res: any) => { setData(res); }).finally(() => { setLoading(false); @@ -42,10 +64,10 @@ function WidgetChart(props: Props) { }, [period]); const renderChart = () => { - const { metricType, viewType, predefinedKey } = metric; + const { metricType, viewType } = metric; if (metricType === 'predefined') { - if (viewType === 'overview') { + if (isOverviewWidget) { return } return @@ -55,16 +77,16 @@ function WidgetChart(props: Props) { if (viewType === 'lineChart') { return ( ) } else if (viewType === 'progress') { return ( @@ -74,12 +96,12 @@ function WidgetChart(props: Props) { if (metricType === 'table') { if (viewType === 'table') { - return ; + return ; } else if (viewType === 'pieChart') { return ( diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index 0ceace3c2..ccfb0fbe7 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -11,12 +11,12 @@ interface Props { function WidgetSessions(props: Props) { const { className = '' } = props; const { dashboardStore } = useStore(); - const period = useObserver(() => dashboardStore.period); + const filter = useObserver(() => dashboardStore.drillDownFilter); const widget = dashboardStore.currentWidget; - const range = period.toTimestamps() - const startTime = DateTime.fromMillis(range.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); - const endTime = DateTime.fromMillis(range.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + // const range = period.toTimestamps() + const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); return useObserver(() => (
diff --git a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx index 8a5391948..d388386e6 100644 --- a/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx +++ b/frontend/app/components/Dashboard/components/WidgetView/WidgetView.tsx @@ -38,7 +38,7 @@ function WidgetView(props: Props) { return useObserver(() => ( -
+
diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index bb47dbc98..6bb9ca875 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -63,6 +63,7 @@ function WidgetWrapper(props: Props) { const onChartClick = () => { if (!isWidget || widget.metricType === 'predefined') return; + props.history.push(withSiteId(dashboardMetricDetails(dashboardId, widget.metricId),siteId)); } diff --git a/frontend/app/components/Funnels/FunnelDetails/FunnelDetails.js b/frontend/app/components/Funnels/FunnelDetails/FunnelDetails.js index 48142ef13..17078976c 100644 --- a/frontend/app/components/Funnels/FunnelDetails/FunnelDetails.js +++ b/frontend/app/components/Funnels/FunnelDetails/FunnelDetails.js @@ -143,7 +143,7 @@ export default connect((state, props) => { funnelId: props.match.params.funnelId, activeStages: state.getIn(['funnels', 'activeStages']), funnelFilters: state.getIn(['funnels', 'funnelFilters']), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), liveFilters: state.getIn(['funnelFilters', 'appliedFilter']), } }, { diff --git a/frontend/app/components/Funnels/FunnelHeader/FunnelDropdown.js b/frontend/app/components/Funnels/FunnelHeader/FunnelDropdown.js index 87f7983b7..a5b3bf445 100644 --- a/frontend/app/components/Funnels/FunnelHeader/FunnelDropdown.js +++ b/frontend/app/components/Funnels/FunnelHeader/FunnelDropdown.js @@ -33,5 +33,5 @@ function FunnelDropdown(props) { export default connect((state, props) => ({ funnels: state.getIn(['funnels', 'list']), funnel: state.getIn(['funnels', 'instance']), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), }), { })(withRouter(FunnelDropdown)) diff --git a/frontend/app/components/Funnels/FunnelIssueDetails/FunnelIssueDetails.js b/frontend/app/components/Funnels/FunnelIssueDetails/FunnelIssueDetails.js index 6672bc580..77017e4ac 100644 --- a/frontend/app/components/Funnels/FunnelIssueDetails/FunnelIssueDetails.js +++ b/frontend/app/components/Funnels/FunnelIssueDetails/FunnelIssueDetails.js @@ -39,5 +39,5 @@ export default connect((state, props) => ({ issue: state.getIn(['funnels', 'issue']), issueId: props.match.params.issueId, funnelId: props.match.params.funnelId, - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), }), { fetchIssue, setNavRef, resetIssue })(withRouter(FunnelIssueDetails)) diff --git a/frontend/app/components/Funnels/FunnelIssues/FunnelIssues.js b/frontend/app/components/Funnels/FunnelIssues/FunnelIssues.js index b46ef824d..036565d60 100644 --- a/frontend/app/components/Funnels/FunnelIssues/FunnelIssues.js +++ b/frontend/app/components/Funnels/FunnelIssues/FunnelIssues.js @@ -73,7 +73,7 @@ export default connect(state => ({ list: state.getIn(['funnels', 'issues']), criticalIssuesCount: state.getIn(['funnels', 'criticalIssuesCount']), loading: state.getIn(['funnels', 'fetchIssuesRequest', 'loading']), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), funnel: state.getIn(['funnels', 'instance']), activeStages: state.getIn(['funnels', 'activeStages']), funnelFilters: state.getIn(['funnels', 'funnelFilters']), diff --git a/frontend/app/components/Funnels/FunnelList/FunnelList.js b/frontend/app/components/Funnels/FunnelList/FunnelList.js index f7e3c70de..c96ef2e74 100644 --- a/frontend/app/components/Funnels/FunnelList/FunnelList.js +++ b/frontend/app/components/Funnels/FunnelList/FunnelList.js @@ -28,5 +28,5 @@ function FunnelList(props) { export default connect(state => ({ list: state.getIn(['funnels', 'list']), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), }))(withRouter(FunnelList)) diff --git a/frontend/app/components/Header/Header.js b/frontend/app/components/Header/Header.js index 9b8819cdc..f3aa726d1 100644 --- a/frontend/app/components/Header/Header.js +++ b/frontend/app/components/Header/Header.js @@ -151,7 +151,7 @@ export default withRouter(connect( state => ({ account: state.getIn([ 'user', 'account' ]), appearance: state.getIn([ 'user', 'account', 'appearance' ]), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), sites: state.getIn([ 'site', 'list' ]), showAlerts: state.getIn([ 'dashboard', 'showAlerts' ]), boardingCompletion: state.getIn([ 'dashboard', 'boardingCompletion' ]) diff --git a/frontend/app/components/Header/OnboardingExplore/OnboardingExplore.js b/frontend/app/components/Header/OnboardingExplore/OnboardingExplore.js index c6d7aa179..d143a6bbd 100644 --- a/frontend/app/components/Header/OnboardingExplore/OnboardingExplore.js +++ b/frontend/app/components/Header/OnboardingExplore/OnboardingExplore.js @@ -37,7 +37,7 @@ const styles = { }; @connect(state => ({ - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), boarding: state.getIn([ 'dashboard', 'boarding' ]), boardingCompletion: state.getIn([ 'dashboard', 'boardingCompletion' ]), }), { diff --git a/frontend/app/components/Header/SiteDropdown.js b/frontend/app/components/Header/SiteDropdown.js index 4f8b9ca60..38fe6ec57 100644 --- a/frontend/app/components/Header/SiteDropdown.js +++ b/frontend/app/components/Header/SiteDropdown.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { setSiteId } from 'Duck/user'; +import { setSiteId } from 'Duck/site'; import { withRouter } from 'react-router-dom'; import { hasSiteId, siteChangeAvaliable } from 'App/routes'; import { STATUS_COLOR_MAP, GREEN } from 'Types/site'; @@ -19,7 +19,7 @@ import { withStore } from 'App/mstore' @withRouter @connect(state => ({ sites: state.getIn([ 'site', 'list' ]), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), account: state.getIn([ 'user', 'account' ]), }), { setSiteId, diff --git a/frontend/app/components/Session/Layout/ToolPanel/StackEvents.js b/frontend/app/components/Session/Layout/ToolPanel/StackEvents.js index 0c10a38d1..6298a8a2e 100644 --- a/frontend/app/components/Session/Layout/ToolPanel/StackEvents.js +++ b/frontend/app/components/Session/Layout/ToolPanel/StackEvents.js @@ -66,7 +66,7 @@ function StackEvents({ export default connect(state => ({ hintIsHidden: state.getIn(['components', 'player', 'hiddenHints', 'stack']) || - !state.getIn([ 'user', 'client', 'sites' ]).some(s => s.stackIntegrations), + !state.getIn([ 'site', 'list' ]).some(s => s.stackIntegrations), }), { hideHint })(StackEvents); \ No newline at end of file diff --git a/frontend/app/components/Session_/Player/Overlay/AutoplayTimer.tsx b/frontend/app/components/Session_/Player/Overlay/AutoplayTimer.tsx index 91ce53722..7bb72c475 100644 --- a/frontend/app/components/Session_/Player/Overlay/AutoplayTimer.tsx +++ b/frontend/app/components/Session_/Player/Overlay/AutoplayTimer.tsx @@ -52,6 +52,6 @@ function AutoplayTimer({ nextId, siteId, history }) { export default withRouter(connect(state => ({ - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), nextId: parseInt(state.getIn([ 'sessions', 'nextId' ])), }))(AutoplayTimer)) diff --git a/frontend/app/components/Session_/PlayerBlockHeader.js b/frontend/app/components/Session_/PlayerBlockHeader.js index abf94e11c..58a524022 100644 --- a/frontend/app/components/Session_/PlayerBlockHeader.js +++ b/frontend/app/components/Session_/PlayerBlockHeader.js @@ -41,7 +41,7 @@ const ASSIST_ROUTE = assistRoute(); issuesFetched: state.getIn([ 'issues', 'issuesFetched' ]), local: state.getIn(['sessions', 'timezone']), funnelRef: state.getIn(['funnels', 'navRef']), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), metaList: state.getIn(['customFields', 'list']).map(i => i.key), closedLive: !!state.getIn([ 'sessions', 'errors' ]) || (isAssist && !session.live), } diff --git a/frontend/app/components/Session_/StackEvents/StackEvents.js b/frontend/app/components/Session_/StackEvents/StackEvents.js index 7145bf7fd..79c1e9c72 100644 --- a/frontend/app/components/Session_/StackEvents/StackEvents.js +++ b/frontend/app/components/Session_/StackEvents/StackEvents.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { connectPlayer } from 'Player'; +import { connectPlayer, jump } from 'Player'; import { NoContent, Tabs } from 'UI'; import withEnumToggle from 'HOCs/withEnumToggle'; import { hideHint } from 'Duck/components/player'; @@ -18,7 +18,7 @@ const TABS = [ ALL, ...typeList ].map(tab =>({ text: tab, key: tab })); })) @connect(state => ({ hintIsHidden: state.getIn(['components', 'player', 'hiddenHints', 'stack']) || - !state.getIn([ 'user', 'client', 'sites' ]).some(s => s.stackIntegrations), + !state.getIn([ 'site', 'list' ]).some(s => s.stackIntegrations), }), { hideHint }) @@ -66,7 +66,11 @@ export default class StackEvents extends React.PureComponent { > { filteredStackEvents.map(userEvent => ( - + jump(userEvent.time) } + /> ))} diff --git a/frontend/app/components/Session_/StackEvents/UserEvent/UserEvent.js b/frontend/app/components/Session_/StackEvents/UserEvent/UserEvent.js index 93a901f0a..9c0e66816 100644 --- a/frontend/app/components/Session_/StackEvents/UserEvent/UserEvent.js +++ b/frontend/app/components/Session_/StackEvents/UserEvent/UserEvent.js @@ -1,6 +1,6 @@ import cn from 'classnames'; import { OPENREPLAY, SENTRY, DATADOG, STACKDRIVER } from 'Types/session/stackEvent'; -import { Modal, Icon, SlideModal } from 'UI'; +import { Modal, Icon, SlideModal, IconButton } from 'UI'; import withToggle from 'HOCs/withToggle'; import Sentry from './Sentry'; import JsonViewer from './JsonViewer'; @@ -54,34 +54,42 @@ export default class UserEvent extends React.PureComponent { return !!this.props.userEvent.payload; } + onClickDetails = (e) => { + e.stopPropagation(); + this.props.switchOpen(); + } + renderContent(modalTrigger) { const { userEvent } = this.props; //const message = this.getEventMessage(); return (
-
-
- - { userEvent.name } -
- { /* message && -
- { message } -
*/ - } -
+ // onClick={ this.props.switchOpen } // + onClick={ this.props.onJump } // + className={ + cn( + "group", + stl.userEvent, + this.getLevelClassname(), + { [ stl.modalTrigger ]: modalTrigger } + ) + } + > +
+
+ + { userEvent.name } +
+ { /* message && +
+ { message } +
*/ + } +
+ +
+
); } @@ -91,15 +99,15 @@ export default class UserEvent extends React.PureComponent { if (this.ifNeedModal()) { return ( - - { this.renderContent(true) } - + + { this.renderContent(true) } + // @withRouter @connect((state, props) => ({ urlSiteId: props.match.params.siteId, - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), }), { setSiteId, }) diff --git a/frontend/app/components/hocs/withSiteIdUpdater.js b/frontend/app/components/hocs/withSiteIdUpdater.js index 67a7dbc60..9319474ca 100644 --- a/frontend/app/components/hocs/withSiteIdUpdater.js +++ b/frontend/app/components/hocs/withSiteIdUpdater.js @@ -1,11 +1,11 @@ import { connect } from 'react-redux'; import { withSiteId } from 'App/routes'; -import { setSiteId } from 'Duck/user'; +import { setSiteId } from 'Duck/site'; export default BaseComponent => @connect((state, props) => ({ urlSiteId: props.match.params.siteId, - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), }), { setSiteId, }) diff --git a/frontend/app/components/shared/SessionItem/SessionItem.js b/frontend/app/components/shared/SessionItem/SessionItem.js index 64e4199ba..35434cf76 100644 --- a/frontend/app/components/shared/SessionItem/SessionItem.js +++ b/frontend/app/components/shared/SessionItem/SessionItem.js @@ -28,7 +28,7 @@ const SESSIONS_ROUTE = sessionsRoute(); // ) @connect(state => ({ timezone: state.getIn(['sessions', 'timezone']), - siteId: state.getIn([ 'user', 'siteId' ]), + siteId: state.getIn([ 'site', 'siteId' ]), }), { toggleFavorite, setSessionPath }) @withRouter export default class SessionItem extends React.PureComponent { diff --git a/frontend/app/components/shared/SiteDropdown/SiteDropdown.js b/frontend/app/components/shared/SiteDropdown/SiteDropdown.js index b83178512..5fc482389 100644 --- a/frontend/app/components/shared/SiteDropdown/SiteDropdown.js +++ b/frontend/app/components/shared/SiteDropdown/SiteDropdown.js @@ -5,7 +5,7 @@ const SiteDropdown = ({ contextName="", sites, onChange, value }) => { const options = sites.map(site => ({ value: site.id, text: site.host })).toJS(); return ( +
+
+ + + ); +} + +export default CallWithErrors; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/Chart.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/Chart.js new file mode 100644 index 000000000..2f406622d --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/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/CallWithErrors/ImageInfo.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/ImageInfo.js new file mode 100644 index 000000000..8251bec60 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/ImageInfo.js @@ -0,0 +1,12 @@ +import { Popup, Icon, TextEllipsis } from 'UI'; +import styles from './imageInfo.css'; + +const ImageInfo = ({ data }) => ( +
+ +
+); + +ImageInfo.displayName = 'ImageInfo'; + +export default ImageInfo; diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/MethodType.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/MethodType.js new file mode 100644 index 000000000..ba370b481 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/MethodType.js @@ -0,0 +1,10 @@ +import React from 'react' +import { Label } from 'UI'; + +const MethodType = ({ data }) => { + return ( + + ) +} + +export default MethodType diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/callWithErrors.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/callWithErrors.css new file mode 100644 index 000000000..bc37a3991 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/callWithErrors.css @@ -0,0 +1,22 @@ +.topActions { + position: absolute; + top: -4px; + right: 50px; + display: flex; + justify-content: flex-end; +} + +.searchField { + padding: 4px 5px; + border-bottom: dotted thin $gray-light; + border-radius: 3px; + &:focus, + &:active { + border: solid thin transparent !important; + box-shadow: none; + background-color: $gray-light; + } + &:hover { + border: solid thin $gray-light !important; + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/imageInfo.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/imageInfo.css new file mode 100644 index 000000000..69030a582 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/imageInfo.css @@ -0,0 +1,39 @@ +.name { + display: flex; + align-items: center; + + & > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 60%; + } +} + +.imagePreview { + max-width: 200px; + max-height: 200px; +} + +.imageWrapper { + display: flex; + flex-flow: column; + align-items: center; + width: 40px; + text-align: center; + margin-right: 10px; + & > span { + height: 16px; + } + & .label { + font-size: 9px; + color: $gray-light; + } +} + +.popup { + background-color: #f5f5f5 !important; + &:before { + background-color: #f5f5f5 !important; + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/index.ts new file mode 100644 index 000000000..4d3ba4df8 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/index.ts @@ -0,0 +1 @@ +export { default } from './CallWithErrors' \ 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 index bd54bc5e3..f1e192587 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx @@ -9,26 +9,26 @@ import { interface Props { data: any + metric?: any } function CallsErrors4xx(props: Props) { - const { data } = props; + const { data, metric } = props; + console.log('asd', metric.data.namesMap) return ( - {/* { data.namesMap.map((key, index) => ( + { Array.isArray(metric.data.namesMap) && metric.data.namesMap.map((key, index) => ( - ))} */} + ))} diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx index e55bbb0cb..3dec42162 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx @@ -9,26 +9,25 @@ import { interface Props { data: any + metric?: any } function CallsErrors5xx(props: Props) { - const { data } = props; + const { data, metric } = props; return ( - {/* { data.namesMap.map((key, index) => ( + { Array.isArray(metric.data.namesMap) && metric.data.namesMap.map((key, index) => ( - ))} */} + ))} diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx index 5dffda63d..b27cf319b 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/Crashes/Crashes.tsx @@ -10,24 +10,24 @@ import { interface Props { data: any + metric?: any } function Crashes(props: Props) { - const { data } = props; + const { data, metric } = props; const gradientDef = Styles.gradientDef(); - const params = { density: 70 } return ( {gradientDef} - + { - const _params = { density: 70 } + // 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 }) } @@ -34,7 +33,7 @@ function DomBuildingTime(props: Props) { return ( <>
@@ -45,7 +44,7 @@ function DomBuildingTime(props: Props) { onSelect={onSelect} placeholder="Search for Page" /> - +
{gradientDef} - +
- {data.chart.map((item, i) => + {metric.data.chart.map((item, i) => <>
@@ -27,12 +27,12 @@ function FPS(props: Props) {
{gradientDef} - + <>
@@ -27,12 +27,12 @@ function MemoryConsumption(props: Props) {
{gradientDef} - +
diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/ResourceLoadedVsResponseEnd.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/ResourceLoadedVsResponseEnd.tsx index 378a3abdc..0423a0007 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/ResourceLoadedVsResponseEnd.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd/ResourceLoadedVsResponseEnd.tsx @@ -8,19 +8,19 @@ import { interface Props { data: any + metric?: any } function ResourceLoadedVsResponseEnd(props: Props) { - const { data } = props; - const params = { density: 70 } + const { data, metric } = props; return ( @@ -28,7 +28,7 @@ function ResourceLoadedVsResponseEnd(props: Props) { {...Styles.xaxis} dataKey="time" // interval={3} - interval={(params.density / 7)} + interval={(metric.params.density / 7)} /> { - const _params = { density: 70 } + // 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 }) @@ -52,7 +52,7 @@ function ResourceLoadingTime(props: Props) { return ( <>
@@ -80,17 +80,17 @@ function ResourceLoadingTime(props: Props) {
{gradientDef} - + Styles.tickFormatter(val)} - label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + label={{ ...Styles.axisLabelLeft, value: "Resource Fetch Time (ms)" }} /> { - const _params = { density: 70 } + // 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 }) } @@ -34,7 +34,7 @@ function ResponseTime(props: Props) { return ( <>
@@ -45,7 +45,7 @@ function ResponseTime(props: Props) { onSelect={onSelect} placeholder="Search for Page" /> - +
{gradientDef} - + {gradientDef} - + Styles.tickFormatter(val)} - label={{ ...Styles.axisLabelLeft, value: "CPU Load (%)" }} + label={{ ...Styles.axisLabelLeft, value: "Number of Requests" }} /> { + return Object.keys(item) + .filter(i => i !== 'browser' && i !== 'count') + .map(i => ({ key: 'v' +i, value: item[i]})) + } + return ( + +
+ {metric.data.chart.map((item, i) => + + )} +
+
+ ); +} + +export default SessionsPerBrowser; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/index.ts new file mode 100644 index 000000000..06f0656a1 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/index.ts @@ -0,0 +1 @@ +export { default } from './SessionsPerBrowser' \ 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 index a7334e650..9e2563752 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx @@ -6,17 +6,18 @@ import Bar from 'App/components/Dashboard/Widgets/SlowestDomains/Bar'; interface Props { data: any + metric?: any } function SlowestDomains(props: Props) { - const { data } = props; - const firstAvg = data.chart[0] && data.chart[0].errorsCount; + const { data, metric } = props; + const firstAvg = metric.data.chart[0] && metric.data.chart[0].errorsCount; return (
- {data.chart.map((item, i) => + {metric.data.chart.map((item, i) => { - const _params = { density: 70 } + // 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 }) } @@ -34,7 +34,7 @@ function TimeToRender(props: Props) { return ( <>
@@ -49,12 +49,12 @@ function TimeToRender(props: Props) {
{gradientDef} - + } - return + return } if (metricType === 'timeseries') { diff --git a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx index 7ae8a581f..151825b80 100644 --- a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx @@ -22,64 +22,69 @@ import ResourceLoadingTime from 'App/components/Dashboard/Widgets/PredefinedWidg 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'; +import SessionsPerBrowser from 'App/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser'; +import CallWithErrors from '../../Widgets/PredefinedWidgets/CallWithErrors'; interface Props { data: any; predefinedKey: string + metric?: any; } function WidgetPredefinedChart(props: Props) { - const { data, predefinedKey } = props; + const { data, predefinedKey, metric } = props; const renderWidget = () => { switch (predefinedKey) { // ERRORS case 'errors_per_type': - return + return case 'errors_per_domains': - return + return case 'resources_by_party': - return + return case 'impacted_sessions_by_js_errors': - return + return case 'domains_errors_4xx': - return + return case 'domains_errors_5xx': - return + return + case 'calls_errors': + return // PERFORMANCE // case 'impacted_sessions_by_slow_pages': // case 'pages_response_time_distribution': // case 'speed_location': case 'cpu': - return + return case 'crashes': - return + return case 'pages_dom_buildtime': - return + return case 'fps': - return + return case 'memory_consumption': - return + return case 'pages_response_time': - return + return case 'resources_vs_visually_complete': - return + return case 'sessions_per_browser': - return + return case 'slowest_domains': - return + return case 'time_to_render': - return + return // Resources case 'resources_count_by_type': - return + return case 'missing_resources': - return + return case 'resource_type_vs_response_end': - return + return case 'resources_loading_time': - return + return // case 'slowest_resources': default: diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 1166b245f..73a41b379 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -420,27 +420,6 @@ 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) - // } - // 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 = { ...data, namesMap: data.namesMap, chart: data.chart } - // metric.setData(_data) - // resolve(_data); - const _data = { ...data, } @@ -457,7 +436,7 @@ export default class DashboardStore implements IDashboardSotre { return unique; }, []) } else { - _data['chart'] = Array.isArray(data) ? data : [] + _data['chart'] = getChartFormatter(this.period)(Array.isArray(data) ? data : []); _data['namesMap'] = Array.isArray(data) ? data.map(i => Object.keys(i)) .flat() .filter(i => i !== 'time' && i !== 'timestamp') diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index ee9c559b4..2c74ce26b 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -28,6 +28,8 @@ export interface IWidget { colSpan: number predefinedKey: string + params: any + udpateKey(key: string, value: any): void removeSeries(index: number): void addSeries(): void @@ -57,6 +59,7 @@ export default class Widget implements IWidget { dashboards: any[] = [] dashboardIds: any[] = [] config: any = {} + params: any = { density: 70 } position: number = 0 data: any = { From d4dbb7c8bc7dde12933c0add27619bd0bb0ccbf3 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 18:55:49 +0200 Subject: [PATCH 139/294] feat(peers): peerjs server as a standalone service --- peers/.gitignore | 6 + peers/Dockerfile | 14 + peers/build.sh | 33 + peers/package-lock.json | 1584 ++++++++++++++++++++++++++++++++ peers/package.json | 24 + peers/server.js | 33 + peers/servers/peerjs-server.js | 65 ++ 7 files changed, 1759 insertions(+) create mode 100644 peers/.gitignore create mode 100644 peers/Dockerfile create mode 100644 peers/build.sh create mode 100644 peers/package-lock.json create mode 100644 peers/package.json create mode 100644 peers/server.js create mode 100644 peers/servers/peerjs-server.js diff --git a/peers/.gitignore b/peers/.gitignore new file mode 100644 index 000000000..a4b05b411 --- /dev/null +++ b/peers/.gitignore @@ -0,0 +1,6 @@ +.idea +node_modules +npm-debug.log +.cache +test.html +/utils/ diff --git a/peers/Dockerfile b/peers/Dockerfile new file mode 100644 index 000000000..d053bf3af --- /dev/null +++ b/peers/Dockerfile @@ -0,0 +1,14 @@ +FROM node:17-stretch +WORKDIR /work +COPY . . +RUN npm install + +# Add Tini +# Startup daemon +ENV TINI_VERSION v0.19.0 +ARG envarg +ENV ENTERPRISE_BUILD ${envarg} +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini +RUN chmod +x /tini +ENTRYPOINT ["/tini", "--"] +CMD npm start \ No newline at end of file diff --git a/peers/build.sh b/peers/build.sh new file mode 100644 index 000000000..c15921ea8 --- /dev/null +++ b/peers/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Script to build api module +# flags to accept: +# Default will be OSS build. + +# Usage: IMAGE_TAG=latest DOCKER_REPO=myDockerHubID bash build.sh + +git_sha1=${IMAGE_TAG:-$(git rev-parse HEAD)} +check_prereq() { + which docker || { + echo "Docker not installed, please install docker." + exit=1 + } + [[ exit -eq 1 ]] && exit 1 +} + +function build_api(){ + cp -R ../utilities/utils . + # Copy enterprise code + [[ $1 == "ee" ]] && { + cp -rf ../ee/peers/* ./ + } + docker build -f ./Dockerfile -t ${DOCKER_REPO:-'local'}/peers:${git_sha1} . + [[ $PUSH_IMAGE -eq 1 ]] && { + docker push ${DOCKER_REPO:-'local'}/peers:${git_sha1} + docker tag ${DOCKER_REPO:-'local'}/peers:${git_sha1} ${DOCKER_REPO:-'local'}/peers:latest + docker push ${DOCKER_REPO:-'local'}/peers:latest + } +} + +check_prereq +build_api $1 diff --git a/peers/package-lock.json b/peers/package-lock.json new file mode 100644 index 000000000..5f253d3bc --- /dev/null +++ b/peers/package-lock.json @@ -0,0 +1,1584 @@ +{ + "name": "utilities_server", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "utilities_server", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "express": "^4.17.1", + "peer": "^0.6.1" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "node_modules/@types/node": { + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==" + }, + "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==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "dependencies": { + "@types/node": "*" + } + }, + "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==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "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/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "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==", + "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/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.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/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==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "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=" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "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==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "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/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "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/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "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==", + "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/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/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==", + "engines": { + "node": ">= 0.10" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "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=", + "engines": { + "node": ">= 0.6" + } + }, + "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=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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==", + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "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/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dependencies": { + "p-limit": "^2.2.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==", + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "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=" + }, + "node_modules/peer": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/peer/-/peer-0.6.1.tgz", + "integrity": "sha512-zPJSPoZvo+83sPJNrW8o93QTktx7dKk67965RRDDNAIelWw1ZwE6ZmmhsvRrdNRlK0knQb3rR8GBdZlbWzCYJw==", + "dependencies": { + "@types/cors": "^2.8.6", + "@types/express": "^4.17.3", + "@types/ws": "^7.2.3", + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "express": "^4.17.1", + "uuid": "^3.4.0", + "ws": "^7.2.3", + "yargs": "^15.3.1" + }, + "bin": { + "peerjs": "bin/peerjs" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/peer/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "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.", + "bin": { + "uuid": "bin/uuid" + } + }, + "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==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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==", + "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/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "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==" + }, + "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==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "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==", + "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=" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "engines": { + "node": ">=0.6" + } + }, + "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==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "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=" + }, + "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==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "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==" + }, + "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==", + "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/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs-parser/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", + "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.28", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz", + "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "@types/node": { + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==" + }, + "@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==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/ws": { + "version": "7.4.7", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", + "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", + "requires": { + "@types/node": "*" + } + }, + "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==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "requires": { + "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" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "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==", + "requires": { + "color-name": "~1.1.4" + } + }, + "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==" + }, + "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==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "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==" + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "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==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "requires": { + "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" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "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" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "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==" + }, + "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==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "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==" + }, + "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==" + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + }, + "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==", + "requires": { + "mime-db": "1.51.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "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==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "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==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "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==" + }, + "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=" + }, + "peer": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/peer/-/peer-0.6.1.tgz", + "integrity": "sha512-zPJSPoZvo+83sPJNrW8o93QTktx7dKk67965RRDDNAIelWw1ZwE6ZmmhsvRrdNRlK0knQb3rR8GBdZlbWzCYJw==", + "requires": { + "@types/cors": "^2.8.6", + "@types/express": "^4.17.3", + "@types/ws": "^7.2.3", + "body-parser": "^1.19.0", + "cors": "^2.8.5", + "express": "^4.17.1", + "uuid": "^3.4.0", + "ws": "^7.2.3", + "yargs": "^15.3.1" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "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==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" + }, + "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==" + }, + "raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "requires": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "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==" + }, + "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==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "requires": { + "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" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "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==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "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==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "ws": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "requires": {} + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "requires": { + "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" + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + } + } + } + } +} diff --git a/peers/package.json b/peers/package.json new file mode 100644 index 000000000..ba29b0899 --- /dev/null +++ b/peers/package.json @@ -0,0 +1,24 @@ +{ + "name": "utilities_server", + "version": "1.0.0", + "description": "assist server to get live sessions & sourcemaps reader to get stack trace", + "main": "peerjs-server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/openreplay/openreplay.git" + }, + "author": "KRAIEM Taha Yassine ", + "license": "MIT", + "bugs": { + "url": "https://github.com/openreplay/openreplay/issues" + }, + "homepage": "https://github.com/openreplay/openreplay#readme", + "dependencies": { + "express": "^4.17.1", + "peer": "^0.6.1" + } +} diff --git a/peers/server.js b/peers/server.js new file mode 100644 index 000000000..39f46d4f1 --- /dev/null +++ b/peers/server.js @@ -0,0 +1,33 @@ +const dumps = require('./utils/HeapSnapshot'); +const {request_logger} = require('./utils/helper'); +const {peerRouter, peerConnection, peerDisconnect, peerError} = require('./servers/peerjs-server'); +const express = require('express'); +const {ExpressPeerServer} = require('peer'); + +const HOST = '0.0.0.0'; +const PORT = 9000; + +const app = express(); + +app.use(request_logger("[app]")); + +app.use(`/${process.env.S3_KEY}/assist`, peerRouter); +app.use(`/${process.env.S3_KEY}/heapdump`, dumps.router); + +const server = app.listen(PORT, HOST, () => { + console.log(`App listening on http://${HOST}:${PORT}`); + console.log('Press Ctrl+C to quit.'); +}); + +const peerServer = ExpressPeerServer(server, { + debug: true, + path: '/', + proxied: true, + allow_discovery: false +}); +peerServer.on('connection', peerConnection); +peerServer.on('disconnect', peerDisconnect); +peerServer.on('error', peerError); +app.use('/', peerServer); +app.enable('trust proxy'); +module.exports = {server}; \ No newline at end of file diff --git a/peers/servers/peerjs-server.js b/peers/servers/peerjs-server.js new file mode 100644 index 000000000..a99ec1665 --- /dev/null +++ b/peers/servers/peerjs-server.js @@ -0,0 +1,65 @@ +const express = require('express'); +const peerRouter = express.Router(); +const {extractPeerId} = require('../utils/helper'); + +let debug = process.env.debug === "1" || false; + +const connectedPeers = {}; + +const peerConnection = (client) => { + debug && console.log(`initiating ${client.id}`); + const {projectKey, sessionId} = extractPeerId(client.id); + if (projectKey === undefined || sessionId === undefined) { + return; + } + connectedPeers[projectKey] = connectedPeers[projectKey] || []; + if (connectedPeers[projectKey].indexOf(sessionId) === -1) { + debug && console.log(`new connexion ${client.id}`); + connectedPeers[projectKey].push(sessionId); + } else { + debug && console.log(`reconnecting peer ${client.id}`); + } + + +}; +const peerDisconnect = (client) => { + debug && console.log(`disconnect ${client.id}`); + const {projectKey, sessionId} = extractPeerId(client.id); + if (projectKey === undefined || sessionId === undefined) { + return; + } + const i = (connectedPeers[projectKey] || []).indexOf(sessionId); + if (i === -1) { + debug && console.log(`session not found ${client.id}`); + } else { + connectedPeers[projectKey].splice(i, 1); + } +} + +const peerError = (error) => { + console.error('error fired'); + console.error(error); +} + + +peerRouter.get(`/peers`, function (req, res) { + debug && console.log("looking for all available sessions"); + res.statusCode = 200; + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({"data": connectedPeers})); +}); +peerRouter.get(`/peers/:projectKey`, function (req, res) { + debug && console.log(`looking for available sessions for ${req.params.projectKey}`); + res.statusCode = 200; + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({"data": connectedPeers[req.params.projectKey] || []})); +}); + + +module.exports = { + peerRouter, + peerConnection, + peerDisconnect, + peerError, + extractPeerId +}; \ No newline at end of file From 364b9c40f26729ac8f38d5f940468b8e3103fb22 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 19:06:19 +0200 Subject: [PATCH 140/294] feat(sourcemaps-reader): extracted sourcemaps reader --- ee/utilities/package.json | 4 +- peers/package.json | 4 +- sourcemap-reader/.gitignore | 6 + sourcemap-reader/package-lock.json | 1113 +++++++++++++++++ sourcemap-reader/package.json | 25 + sourcemap-reader/server.js | 19 + .../servers/sourcemaps-handler.js | 104 ++ sourcemap-reader/servers/sourcemaps-server.js | 30 + utilities/package.json | 4 +- 9 files changed, 1303 insertions(+), 6 deletions(-) create mode 100644 sourcemap-reader/.gitignore create mode 100644 sourcemap-reader/package-lock.json create mode 100644 sourcemap-reader/package.json create mode 100644 sourcemap-reader/server.js create mode 100644 sourcemap-reader/servers/sourcemaps-handler.js create mode 100644 sourcemap-reader/servers/sourcemaps-server.js diff --git a/ee/utilities/package.json b/ee/utilities/package.json index ec895470b..99c2666da 100644 --- a/ee/utilities/package.json +++ b/ee/utilities/package.json @@ -1,5 +1,5 @@ { - "name": "utilities_server", + "name": "utilities-server", "version": "1.0.0", "description": "assist server to get live sessions & sourcemaps reader to get stack trace", "main": "peerjs-server.js", @@ -12,7 +12,7 @@ "url": "git+https://github.com/openreplay/openreplay.git" }, "author": "KRAIEM Taha Yassine ", - "license": "MIT", + "license": "Elastic License 2.0 (ELv2)", "bugs": { "url": "https://github.com/openreplay/openreplay/issues" }, diff --git a/peers/package.json b/peers/package.json index ba29b0899..8a87b3b6a 100644 --- a/peers/package.json +++ b/peers/package.json @@ -1,5 +1,5 @@ { - "name": "utilities_server", + "name": "peers-server", "version": "1.0.0", "description": "assist server to get live sessions & sourcemaps reader to get stack trace", "main": "peerjs-server.js", @@ -12,7 +12,7 @@ "url": "git+https://github.com/openreplay/openreplay.git" }, "author": "KRAIEM Taha Yassine ", - "license": "MIT", + "license": "Elastic License 2.0 (ELv2)", "bugs": { "url": "https://github.com/openreplay/openreplay/issues" }, diff --git a/sourcemap-reader/.gitignore b/sourcemap-reader/.gitignore new file mode 100644 index 000000000..a4b05b411 --- /dev/null +++ b/sourcemap-reader/.gitignore @@ -0,0 +1,6 @@ +.idea +node_modules +npm-debug.log +.cache +test.html +/utils/ diff --git a/sourcemap-reader/package-lock.json b/sourcemap-reader/package-lock.json new file mode 100644 index 000000000..e2871acb2 --- /dev/null +++ b/sourcemap-reader/package-lock.json @@ -0,0 +1,1113 @@ +{ + "name": "sourcemaps-reader", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "sourcemaps-reader", + "version": "1.0.0", + "license": "Elastic License 2.0 (ELv2)", + "dependencies": { + "aws-sdk": "^2.992.0", + "express": "^4.17.1", + "source-map": "^0.7.3" + } + }, + "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==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/aws-sdk": { + "version": "2.1087.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1087.0.tgz", + "integrity": "sha512-m5EERT29Fwh2cv3SaSdygeAjJBXnjSaXRRERy70bf6PQ7KgmASJouBxY11g5G7LTEPK/yfB0TGshujKh3hEtPA==", + "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/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "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/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "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=" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "engines": { + "node": ">=0.4.x" + } + }, + "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==", + "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/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "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/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "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/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==" + }, + "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/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==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "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==", + "engines": { + "node": ">= 0.6.0" + } + }, + "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=", + "engines": { + "node": ">= 0.6" + } + }, + "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=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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==", + "dependencies": { + "mime-db": "1.51.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "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=" + }, + "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==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "node_modules/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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.", + "engines": { + "node": ">=0.4.x" + } + }, + "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==", + "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==", + "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/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==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "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==", + "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/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "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/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "engines": { + "node": ">=0.6" + } + }, + "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==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "dependencies": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "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.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "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=", + "engines": { + "node": ">=4.0" + } + } + }, + "dependencies": { + "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==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "aws-sdk": { + "version": "2.1087.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1087.0.tgz", + "integrity": "sha512-m5EERT29Fwh2cv3SaSdygeAjJBXnjSaXRRERy70bf6PQ7KgmASJouBxY11g5G7LTEPK/yfB0TGshujKh3hEtPA==", + "requires": { + "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" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "requires": { + "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" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "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==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "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==" + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "requires": { + "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" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "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" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "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==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "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==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + }, + "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==", + "requires": { + "mime-db": "1.51.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "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=" + }, + "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==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "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==" + }, + "raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "requires": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "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==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "requires": { + "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" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } +} diff --git a/sourcemap-reader/package.json b/sourcemap-reader/package.json new file mode 100644 index 000000000..8ed3189bb --- /dev/null +++ b/sourcemap-reader/package.json @@ -0,0 +1,25 @@ +{ + "name": "sourcemaps-reader", + "version": "1.0.0", + "description": "assist server to get live sessions & sourcemaps reader to get stack trace", + "main": "peerjs-server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/openreplay/openreplay.git" + }, + "author": "KRAIEM Taha Yassine ", + "license": "Elastic License 2.0 (ELv2)", + "bugs": { + "url": "https://github.com/openreplay/openreplay/issues" + }, + "homepage": "https://github.com/openreplay/openreplay#readme", + "dependencies": { + "aws-sdk": "^2.992.0", + "express": "^4.17.1", + "source-map": "^0.7.3" + } +} diff --git a/sourcemap-reader/server.js b/sourcemap-reader/server.js new file mode 100644 index 000000000..073cb4cfc --- /dev/null +++ b/sourcemap-reader/server.js @@ -0,0 +1,19 @@ +const dumps = require('./utils/HeapSnapshot'); +const sourcemapsReaderServer = require('./servers/sourcemaps-server'); +const express = require('express'); +const {request_logger} = require("./utils/helper"); + +const HOST = '0.0.0.0'; +const PORT = 9000; + +const app = express(); +app.use(request_logger("[wsapp]")); + +app.use('/sourcemaps', sourcemapsReaderServer); +app.use('/heapdump', dumps.router); + +const server = app.listen(PORT, HOST, () => { + console.log(`WS App listening on http://${HOST}:${PORT}`); + console.log('Press Ctrl+C to quit.'); +}); +module.exports = {server}; \ No newline at end of file diff --git a/sourcemap-reader/servers/sourcemaps-handler.js b/sourcemap-reader/servers/sourcemaps-handler.js new file mode 100644 index 000000000..91917104d --- /dev/null +++ b/sourcemap-reader/servers/sourcemaps-handler.js @@ -0,0 +1,104 @@ +'use strict'; +const sourceMap = require('source-map'); +const AWS = require('aws-sdk'); +const sourceMapVersion = require('../package.json').dependencies["source-map"]; +const URL = require('url'); +const getVersion = version => version.replace(/[\^\$\=\~]/, ""); + +module.exports.sourcemapReader = async event => { + sourceMap.SourceMapConsumer.initialize({ + "lib/mappings.wasm": `https://unpkg.com/source-map@${getVersion(sourceMapVersion)}/lib/mappings.wasm` + }); + let s3; + if (event.S3_HOST) { + s3 = new AWS.S3({ + endpoint: event.S3_HOST, + accessKeyId: event.S3_KEY, + secretAccessKey: event.S3_SECRET, + region: event.region, + s3ForcePathStyle: true, // needed with minio? + signatureVersion: 'v4' + }); + } else if (process.env.S3_HOST) { + s3 = new AWS.S3({ + endpoint: process.env.S3_HOST, + accessKeyId: process.env.S3_KEY, + secretAccessKey: process.env.S3_SECRET, + s3ForcePathStyle: true, // needed with minio? + signatureVersion: 'v4' + }); + } else { + s3 = new AWS.S3({ + 'AccessKeyID': process.env.aws_access_key_id, + 'SecretAccessKey': process.env.aws_secret_access_key, + 'Region': process.env.aws_region + }); + } + + var options = { + Bucket: event.bucket, + Key: event.key + }; + return new Promise(function (resolve, reject) { + s3.getObject(options, (err, data) => { + if (err) { + console.log("Get S3 object failed"); + console.log(err); + return reject(err); + } + let sourcemap = data.Body.toString(); + + return new sourceMap.SourceMapConsumer(sourcemap) + .then(consumer => { + let results = []; + for (let i = 0; i < event.positions.length; i++) { + let original = consumer.originalPositionFor({ + line: event.positions[i].line, + column: event.positions[i].column + }); + let url = URL.parse(""); + let preview = []; + if (original.source) { + preview = consumer.sourceContentFor(original.source, true); + if (preview !== null) { + preview = preview.split("\n") + .map((line, i) => [i + 1, line]); + if (event.padding) { + let start = original.line < event.padding ? 0 : original.line - event.padding; + preview = preview.slice(start, original.line + event.padding); + } + } else { + console.log("source not found, null preview for:"); + console.log(original.source); + preview = [] + } + url = URL.parse(original.source); + } else { + console.log("couldn't find original position of:"); + console.log({ + line: event.positions[i].line, + column: event.positions[i].column + }); + } + let result = { + "absPath": url.href, + "filename": url.pathname, + "lineNo": original.line, + "colNo": original.column, + "function": original.name, + "context": preview + }; + // console.log(result); + results.push(result); + } + consumer = undefined; + // Use this code if you don't use the http event with the LAMBDA-PROXY integration + return resolve(results); + }) + .finally(() => { + sourcemap = undefined; + }) + + }); + }); +}; \ No newline at end of file diff --git a/sourcemap-reader/servers/sourcemaps-server.js b/sourcemap-reader/servers/sourcemaps-server.js new file mode 100644 index 000000000..a1dd501a0 --- /dev/null +++ b/sourcemap-reader/servers/sourcemaps-server.js @@ -0,0 +1,30 @@ +var express = require('express'); +var handler = require('./sourcemaps-handler'); +var router = express.Router(); + +router.post('/', (req, res) => { + let data = ''; + req.on('data', chunk => { + data += chunk; + }); + req.on('end', function () { + data = JSON.parse(data); + console.log("Starting parser for: " + data.key); + // process.env = {...process.env, ...data.bucket_config}; + handler.sourcemapReader(data) + .then((results) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify(results)); + }) + .catch((e) => { + console.error("Something went wrong"); + console.error(e); + res.statusCode(500); + res.end(e); + }); + }) + +}); + +module.exports = router; \ No newline at end of file diff --git a/utilities/package.json b/utilities/package.json index 483f1b4aa..73b92d0f2 100644 --- a/utilities/package.json +++ b/utilities/package.json @@ -1,5 +1,5 @@ { - "name": "utilities_server", + "name": "utilities-server", "version": "1.0.0", "description": "assist server to get live sessions & sourcemaps reader to get stack trace", "main": "peerjs-server.js", @@ -12,7 +12,7 @@ "url": "git+https://github.com/openreplay/openreplay.git" }, "author": "KRAIEM Taha Yassine ", - "license": "MIT", + "license": "Elastic License 2.0 (ELv2)", "bugs": { "url": "https://github.com/openreplay/openreplay/issues" }, From 7308ba2c1868db32b8f3002ae4306901590cbcf9 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 19:16:24 +0200 Subject: [PATCH 141/294] feat(api): merged sourcemaps reader with API build --- api/.env.default | 2 +- api/Dockerfile | 8 ++++++++ api/build.sh | 2 ++ api/entrypoint.sh | 3 +++ ee/api/.env.default | 2 +- ee/api/.gitignore | 1 + ee/api/Dockerfile | 8 ++++++++ ee/api/entrypoint.sh | 2 -- 8 files changed, 24 insertions(+), 4 deletions(-) delete mode 100755 ee/api/entrypoint.sh diff --git a/api/.env.default b/api/.env.default index 0d4f8130c..6caeb96d8 100644 --- a/api/.env.default +++ b/api/.env.default @@ -44,6 +44,6 @@ sentryURL= sessions_bucket=mobs sessions_region=us-east-1 sourcemaps_bucket=sourcemaps -sourcemaps_reader=http://utilities-openreplay.app.svc.cluster.local:9000/sourcemaps +sourcemaps_reader=http://127.0.0.1:9000/ stage=default-foss version_number=1.4.0 \ No newline at end of file diff --git a/api/Dockerfile b/api/Dockerfile index 4526c32bd..0673ab2b5 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -6,6 +6,14 @@ COPY . . RUN pip install -r requirements.txt RUN mv .env.default .env ENV APP_NAME chalice +# Installing Nodejs +RUN apt update && apt install -y curl && \ + curl -fsSL https://deb.nodesource.com/setup_12.x | bash - && \ + apt install -y nodejs && \ + apt remove --purge -y curl && \ + rm -rf /var/lib/apt/lists/* && \ + cd sourcemap-reader && \ + npm install # Add Tini # Startup daemon diff --git a/api/build.sh b/api/build.sh index 29b8911ca..cec7525f5 100644 --- a/api/build.sh +++ b/api/build.sh @@ -18,6 +18,8 @@ check_prereq() { } function build_api(){ + cp -R ../utilities/utils ../sourcemap-reader/. + cp -R ../sourcemap-reader . tag="" # Copy enterprise code [[ $1 == "ee" ]] && { diff --git a/api/entrypoint.sh b/api/entrypoint.sh index a092737be..fe5912f0f 100755 --- a/api/entrypoint.sh +++ b/api/entrypoint.sh @@ -1,2 +1,5 @@ #!/bin/bash +cd sourcemap-reader +nohup npm start &> /tmp/sourcemap-reader.log & +cd .. uvicorn app:app --host 0.0.0.0 --reload diff --git a/ee/api/.env.default b/ee/api/.env.default index 778a8f32c..b4b1ad291 100644 --- a/ee/api/.env.default +++ b/ee/api/.env.default @@ -53,6 +53,6 @@ sentryURL= sessions_bucket=mobs sessions_region=us-east-1 sourcemaps_bucket=sourcemaps -sourcemaps_reader=http://utilities-openreplay.app.svc.cluster.local:9000/sourcemaps +sourcemaps_reader=http://127.0.0.1:9000/ stage=default-ee version_number=1.0.0 diff --git a/ee/api/.gitignore b/ee/api/.gitignore index 0c649b68e..f8ff0f789 100644 --- a/ee/api/.gitignore +++ b/ee/api/.gitignore @@ -261,3 +261,4 @@ Pipfile /routers/subs/metrics.py /routers/subs/v1_api.py /chalicelib/core/dashboards2.py +entrypoint.sh \ No newline at end of file diff --git a/ee/api/Dockerfile b/ee/api/Dockerfile index ee88ee22c..aee6aecb2 100644 --- a/ee/api/Dockerfile +++ b/ee/api/Dockerfile @@ -7,6 +7,14 @@ COPY . . RUN pip install -r requirements.txt RUN mv .env.default .env ENV APP_NAME chalice +# Installing Nodejs +RUN apt update && apt install -y curl && \ + curl -fsSL https://deb.nodesource.com/setup_12.x | bash - && \ + apt install -y nodejs && \ + apt remove --purge -y curl && \ + rm -rf /var/lib/apt/lists/* && \ + cd sourcemap-reader && \ + npm install # Add Tini # Startup daemon diff --git a/ee/api/entrypoint.sh b/ee/api/entrypoint.sh deleted file mode 100755 index a092737be..000000000 --- a/ee/api/entrypoint.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -uvicorn app:app --host 0.0.0.0 --reload From 8429118521181418819d8e3f4983188abef3eaac Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 19:22:55 +0200 Subject: [PATCH 142/294] feat(api): dashboard limit errors_per_domains widget to 5 --- api/routers/subs/dashboard.py | 76 ++++++++++++++--------------- ee/api/chalicelib/core/dashboard.py | 2 +- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index e2d4ba268..4e61c3a4e 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -352,46 +352,46 @@ 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.TemplatePredefinedKeys.count_sessions, - "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_image_load_time, - "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_page_load_time, - "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_request_load_time, - "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, - {"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.TemplatePredefinedKeys.avg_first_contentful_pixel, - "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_visited_pages, - "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_session_duration, - "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime, - "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_pages_response_time, - "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_response_time, - "data": dashboard.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_first_paint, - "data": dashboard.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.count_sessions, + # "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_image_load_time, + # "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_page_load_time, + # "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_request_load_time, + # "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, + # {"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.TemplatePredefinedKeys.avg_first_contentful_pixel, + # "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_visited_pages, + # "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_session_duration, + # "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime, + # "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_pages_response_time, + # "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_response_time, + # "data": dashboard.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_first_paint, + # "data": dashboard.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_dom_content_loaded, "data": dashboard.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_till_first_bit, - "data": dashboard.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_time_to_interactive, - "data": dashboard.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.count_requests, - "data": dashboard.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_time_to_render, - "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_used_js_heap_size, - "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_cpu, - "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, - {"key": schemas.TemplatePredefinedKeys.avg_fps, - "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + # {"key": schemas.TemplatePredefinedKeys.avg_till_first_bit, + # "data": dashboard.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_time_to_interactive, + # "data": dashboard.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.count_requests, + # "data": dashboard.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_time_to_render, + # "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_used_js_heap_size, + # "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, + # {"key": schemas.TemplatePredefinedKeys.avg_cpu, + # "data": dashboard.get_avg_cpu(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} diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index dc99ac0d2..404aef795 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -1690,7 +1690,7 @@ def get_errors_per_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1) WHERE {" AND ".join(ch_sub_query)} GROUP BY resources.url_host ORDER BY errors_count DESC - LIMIT 10;""" + LIMIT 5;""" rows = ch.execute(query=ch_query, params={"project_id": project_id, "startTimestamp": startTimestamp, From 34ece1f38eeb612cc1515acfe3fcccbe6e9576a6 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 13 Apr 2022 19:24:15 +0200 Subject: [PATCH 143/294] feat(api): dashboard limit errors_per_domains widget to 5 --- api/chalicelib/core/dashboard.py | 2 +- api/routers/subs/dashboard.py | 76 ++++++++++++++++---------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 66ee770d8..719fabb3a 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -1784,7 +1784,7 @@ def get_errors_per_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1) WHERE {" AND ".join(pg_sub_query)} GROUP BY resources.url_host ORDER BY errors_count DESC - LIMIT 10;""" + LIMIT 5;""" cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)})) diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 4e61c3a4e..e2d4ba268 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -352,46 +352,46 @@ 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.TemplatePredefinedKeys.count_sessions, - # "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_image_load_time, - # "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_page_load_time, - # "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_request_load_time, - # "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, - # {"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.TemplatePredefinedKeys.avg_first_contentful_pixel, - # "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_visited_pages, - # "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_session_duration, - # "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime, - # "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_pages_response_time, - # "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_response_time, - # "data": dashboard.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_first_paint, - # "data": dashboard.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.count_sessions, + "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_image_load_time, + "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_page_load_time, + "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_request_load_time, + "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, + {"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.TemplatePredefinedKeys.avg_first_contentful_pixel, + "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_visited_pages, + "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_session_duration, + "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime, + "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_pages_response_time, + "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_response_time, + "data": dashboard.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_first_paint, + "data": dashboard.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_dom_content_loaded, "data": dashboard.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_till_first_bit, - # "data": dashboard.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_time_to_interactive, - # "data": dashboard.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.count_requests, - # "data": dashboard.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_time_to_render, - # "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_used_js_heap_size, - # "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_cpu, - # "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, - # {"key": schemas.TemplatePredefinedKeys.avg_fps, - # "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + {"key": schemas.TemplatePredefinedKeys.avg_till_first_bit, + "data": dashboard.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_time_to_interactive, + "data": dashboard.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.count_requests, + "data": dashboard.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_time_to_render, + "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_used_js_heap_size, + "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, + {"key": schemas.TemplatePredefinedKeys.avg_cpu, + "data": dashboard.get_avg_cpu(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} From 2bb97b3ddff004b127c7d5dba6a4c8abb747dc4c Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Wed, 13 Apr 2022 21:53:13 +0200 Subject: [PATCH 144/294] feat(backend): pprof on http worker --- backend/services/http/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/services/http/main.go b/backend/services/http/main.go index f0ae22d32..d11d0d323 100644 --- a/backend/services/http/main.go +++ b/backend/services/http/main.go @@ -21,6 +21,8 @@ import ( "openreplay/backend/pkg/url/assets" "openreplay/backend/services/http/geoip" "openreplay/backend/services/http/uaparser" + + _ "net/http/pprof" ) var rewriter *assets.Rewriter From fb60c5d085e3c6bf7f66c5ff007f31a3658692cf Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Wed, 13 Apr 2022 23:02:12 +0200 Subject: [PATCH 145/294] fix(backend):pprof server start --- backend/pkg/pprof/pprof.go | 13 +++++++++++++ backend/services/http/main.go | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 backend/pkg/pprof/pprof.go diff --git a/backend/pkg/pprof/pprof.go b/backend/pkg/pprof/pprof.go new file mode 100644 index 000000000..a05080178 --- /dev/null +++ b/backend/pkg/pprof/pprof.go @@ -0,0 +1,13 @@ +package pprof + +import ( + "log" + "net/http" + _ "net/http/pprof" +) + +func StartProfilingServer() { + go func() { + log.Println(http.ListenAndServe("localhost:6060", nil)) + }() +} diff --git a/backend/services/http/main.go b/backend/services/http/main.go index d11d0d323..1f3bc93b3 100644 --- a/backend/services/http/main.go +++ b/backend/services/http/main.go @@ -22,7 +22,7 @@ import ( "openreplay/backend/services/http/geoip" "openreplay/backend/services/http/uaparser" - _ "net/http/pprof" + "openreplay/backend/pkg/pprof" ) var rewriter *assets.Rewriter @@ -45,6 +45,7 @@ var BEACON_SIZE_LIMIT int64 func main() { log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile) + pprof.StartProfilingServer() producer = queue.NewProducer() defer producer.Close(15000) From 74833dd671c6618ea3a4d3a2a39c03b2dada7c3a Mon Sep 17 00:00:00 2001 From: Rajesh Rajendran Date: Thu, 14 Apr 2022 10:06:40 +0000 Subject: [PATCH 146/294] Kubernetes native ingress (#417) * chore(ingress): changing to nginx-ingress controller https://github.com/openreplay/openreplay/issues/343 Signed-off-by: rjshrjndrn * chore(ingress): Migrating nginx ingress to kube native Signed-off-by: rjshrjndrn * chore(ingress): depricating old ingress * chore(ingress): frontend path TODO: have to migrate frontend from minio to nginx micro container Signed-off-by: rjshrjndrn * fix(ingress): assist port Signed-off-by: rjshrjndrn * chore(helm): nginx-ingress certissuer * chore(helm): update default certificate Signed-off-by: rjshrjndrn * chore(helm): removed old nginx-ingress * chore(helm): make ingress annotaion name same as the ingress class Signed-off-by: rjshrjndrn * chore(initsh): installing certmanager of SSL Signed-off-by: rjshrjndrn --- scripts/helmcharts/certmanager.sh | 34 + scripts/helmcharts/init.sh | 1 - scripts/helmcharts/openreplay/Chart.yaml | 5 + .../charts/assets/templates/ingress.yaml | 49 +- .../openreplay/charts/assets/values.yaml | 15 +- .../charts/chalice/templates/ingress.yaml | 49 +- .../openreplay/charts/chalice/values.yaml | 15 +- .../charts/http/templates/ingress.yaml | 108 +- .../openreplay/charts/http/values.yaml | 21 +- .../.helmignore | 1 - .../charts/ingress-nginx/CHANGELOG.md | 375 +++++++ .../charts/ingress-nginx/Chart.yaml | 25 + .../openreplay/charts/ingress-nginx/OWNERS | 10 + .../openreplay/charts/ingress-nginx/README.md | 486 +++++++++ .../charts/ingress-nginx/README.md.gotmpl | 235 +++++ .../controller-custom-ingressclass-flags.yaml | 7 + .../ci/daemonset-customconfig-values.yaml | 14 + .../ci/daemonset-customnodeport-values.yaml | 22 + .../ci/daemonset-extra-modules.yaml | 10 + .../ci/daemonset-headers-values.yaml | 14 + .../ci/daemonset-internal-lb-values.yaml | 14 + .../ci/daemonset-nodeport-values.yaml | 10 + .../ci/daemonset-podannotations-values.yaml | 17 + ...set-tcp-udp-configMapNamespace-values.yaml | 20 + .../ci/daemonset-tcp-udp-values.yaml | 16 + .../ci/daemonset-tcp-values.yaml | 14 + .../ci/deamonset-default-values.yaml | 10 + .../ci/deamonset-metrics-values.yaml | 12 + .../ci/deamonset-psp-values.yaml | 13 + .../ci/deamonset-webhook-and-psp-values.yaml | 13 + .../ci/deamonset-webhook-values.yaml | 10 + ...eployment-autoscaling-behavior-values.yaml | 14 + .../ci/deployment-autoscaling-values.yaml | 11 + .../ci/deployment-customconfig-values.yaml | 12 + .../ci/deployment-customnodeport-values.yaml | 20 + .../ci/deployment-default-values.yaml | 8 + .../ci/deployment-extra-modules.yaml | 10 + .../ci/deployment-headers-values.yaml | 13 + .../ci/deployment-internal-lb-values.yaml | 13 + .../ci/deployment-metrics-values.yaml | 11 + .../ci/deployment-nodeport-values.yaml | 9 + .../ci/deployment-podannotations-values.yaml | 16 + .../ci/deployment-psp-values.yaml | 10 + ...ent-tcp-udp-configMapNamespace-values.yaml | 19 + .../ci/deployment-tcp-udp-values.yaml | 15 + .../ci/deployment-tcp-values.yaml | 11 + .../ci/deployment-webhook-and-psp-values.yaml | 12 + .../deployment-webhook-resources-values.yaml | 23 + .../ci/deployment-webhook-values.yaml | 9 + .../charts/ingress-nginx/templates/NOTES.txt | 80 ++ .../ingress-nginx/templates/_helpers.tpl | 156 +++ .../ingress-nginx/templates/_params.tpl | 62 ++ .../job-patch/clusterrole.yaml | 34 + .../job-patch/clusterrolebinding.yaml | 23 + .../job-patch/job-createSecret.yaml | 76 ++ .../job-patch/job-patchWebhook.yaml | 78 ++ .../admission-webhooks/job-patch/psp.yaml | 39 + .../admission-webhooks/job-patch/role.yaml | 24 + .../job-patch/rolebinding.yaml | 24 + .../job-patch/serviceaccount.yaml | 16 + .../validating-webhook.yaml | 48 + .../ingress-nginx/templates/clusterrole.yaml | 87 ++ .../templates/clusterrolebinding.yaml | 19 + .../controller-configmap-addheaders.yaml | 14 + .../controller-configmap-proxyheaders.yaml | 19 + .../templates/controller-configmap-tcp.yaml | 17 + .../templates/controller-configmap-udp.yaml | 17 + .../templates/controller-configmap.yaml | 29 + .../templates/controller-daemonset.yaml | 227 +++++ .../templates/controller-deployment.yaml | 225 +++++ .../templates/controller-hpa.yaml | 52 + .../templates/controller-ingressclass.yaml | 21 + .../templates/controller-keda.yaml | 42 + .../controller-poddisruptionbudget.yaml | 19 + .../templates/controller-prometheusrules.yaml | 21 + .../templates/controller-psp.yaml | 89 ++ .../templates/controller-role.yaml | 93 ++ .../templates/controller-rolebinding.yaml | 21 + .../controller-service-internal.yaml | 79 ++ .../templates/controller-service-metrics.yaml | 45 + .../templates/controller-service-webhook.yaml | 40 + .../templates/controller-service.yaml | 101 ++ .../templates/controller-serviceaccount.yaml | 18 + .../templates/controller-servicemonitor.yaml | 48 + .../templates/default-backend-deployment.yaml | 118 +++ .../templates/default-backend-hpa.yaml | 33 + .../default-backend-poddisruptionbudget.yaml | 21 + .../templates/default-backend-psp.yaml | 36 + .../templates/default-backend-role.yaml | 22 + .../default-backend-rolebinding.yaml | 21 + .../templates/default-backend-service.yaml | 41 + .../default-backend-serviceaccount.yaml | 14 + .../templates/dh-param-secret.yaml | 10 + .../charts/ingress-nginx/values.yaml | 919 ++++++++++++++++++ .../charts/nginx-ingress/Chart.yaml | 24 - .../charts/nginx-ingress/files/site.crt | 1 - .../charts/nginx-ingress/files/site.key | 1 - .../charts/nginx-ingress/templates/NOTES.txt | 22 - .../nginx-ingress/templates/_helpers.tpl | 62 -- .../nginx-ingress/templates/configMap.yaml | 190 ---- .../nginx-ingress/templates/deployment.yaml | 80 -- .../charts/nginx-ingress/templates/hpa.yaml | 28 - .../nginx-ingress/templates/ingress.yaml | 61 -- .../nginx-ingress/templates/secrets.yaml | 9 - .../nginx-ingress/templates/service.yaml | 41 - .../templates/serviceaccount.yaml | 12 - .../templates/tests/test-connection.yaml | 15 - .../charts/nginx-ingress/values.yaml | 109 --- .../charts/utilities/templates/ingress.yaml | 62 +- .../openreplay/charts/utilities/values.yaml | 17 +- scripts/helmcharts/vars.yaml | 21 + 111 files changed, 5017 insertions(+), 857 deletions(-) create mode 100644 scripts/helmcharts/certmanager.sh rename scripts/helmcharts/openreplay/charts/{nginx-ingress => ingress-nginx}/.helmignore (97%) create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/CHANGELOG.md create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/Chart.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/OWNERS create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/README.md create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/README.md.gotmpl create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/controller-custom-ingressclass-flags.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-extra-modules.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-headers-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-default-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-metrics-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-psp-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-webhook-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-autoscaling-behavior-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-customconfig-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-default-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-extra-modules.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-headers-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-metrics-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-nodeport-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-podannotations-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-psp-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-resources-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-values.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/NOTES.txt create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/_helpers.tpl create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/_params.tpl create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/clusterrole.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/clusterrolebinding.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-tcp.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-udp.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-daemonset.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-deployment.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-hpa.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-ingressclass.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-keda.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-prometheusrules.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-psp.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-role.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-rolebinding.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-internal.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-metrics.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-webhook.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-serviceaccount.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-servicemonitor.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-deployment.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-hpa.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-psp.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-role.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-rolebinding.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-service.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/templates/dh-param-secret.yaml create mode 100644 scripts/helmcharts/openreplay/charts/ingress-nginx/values.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/Chart.yaml delete mode 120000 scripts/helmcharts/openreplay/charts/nginx-ingress/files/site.crt delete mode 120000 scripts/helmcharts/openreplay/charts/nginx-ingress/files/site.key delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/NOTES.txt delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/_helpers.tpl delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/configMap.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/deployment.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/hpa.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/ingress.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/secrets.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/service.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/serviceaccount.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/templates/tests/test-connection.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml diff --git a/scripts/helmcharts/certmanager.sh b/scripts/helmcharts/certmanager.sh new file mode 100644 index 000000000..23f138084 --- /dev/null +++ b/scripts/helmcharts/certmanager.sh @@ -0,0 +1,34 @@ +# --- helper functions for logs --- +info() +{ + echo '[INFO] ' "$@" +} +warn() +{ + echo '[WARN] ' "$@" >&2 +} +fatal() +{ + echo '[ERROR] ' "$@" >&2 + exit 1 +} + +# Reading email address for ssl certificate +[[ -z $EMAIL_ADDRESS ]] && { + read -p "Enter your email address for letsencrypt certificate: " EMAIL_ADDRESS + echo +} +if [[ "$EMAIL_ADDRESS" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$ ]] +then + info "Email address $EMAIL_ADDRESS is valid." +else + fatal "Email address $EMAIL_ADDRESS is invalid." +fi + +sed -i "s/email: \"\"/email: \"${EMAIL_ADDRESS}\"/g" clusterIssuer.yaml +info "Installing cert-manager for auto letsencrypt certificate" +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.crds.yaml +helm repo add jetstack https://charts.jetstack.io +helm repo update +helm upgrade --install cert-manager --namespace cert-manager --version v1.8.0 jetstack/cert-manager --create-namespace +kubectl apply -f clusterIssuer.yaml diff --git a/scripts/helmcharts/init.sh b/scripts/helmcharts/init.sh index 281e8586b..c772629fc 100644 --- a/scripts/helmcharts/init.sh +++ b/scripts/helmcharts/init.sh @@ -81,7 +81,6 @@ sed -i "s/secretKey: \"changeMeMinioPassword\"/secretKey: \"$(randomPass)\"/g" v sed -i "s/jwt_secret: \"SetARandomStringHere\"/jwt_secret: \"$(randomPass)\"/g" vars.yaml sed -i "s/domainName: \"\"/domainName: \"${DOMAIN_NAME}\"/g" vars.yaml - ## Installing OpenReplay info "Installing databases" helm upgrade --install databases ./databases -n db --create-namespace --wait -f ./vars.yaml --atomic diff --git a/scripts/helmcharts/openreplay/Chart.yaml b/scripts/helmcharts/openreplay/Chart.yaml index be16d6344..827f8566e 100644 --- a/scripts/helmcharts/openreplay/Chart.yaml +++ b/scripts/helmcharts/openreplay/Chart.yaml @@ -23,3 +23,8 @@ version: 0.1.0 # It is recommended to use it with quotes. # Ref: https://github.com/helm/helm/issues/7858#issuecomment-608114589 AppVersion: "v1.5.4" + +dependencies: + - name: ingress-nginx + version: "4.x.x" + repository: "https://kubernetes.github.io/ingress-nginx" diff --git a/scripts/helmcharts/openreplay/charts/assets/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/assets/templates/ingress.yaml index 62c421a9a..3c6ae561b 100644 --- a/scripts/helmcharts/openreplay/charts/assets/templates/ingress.yaml +++ b/scripts/helmcharts/openreplay/charts/assets/templates/ingress.yaml @@ -1,61 +1,34 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled }} {{- $fullName := include "assets.fullname" . -}} {{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} kind: Ingress metadata: name: {{ $fullName }} labels: {{- include "assets.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$1 + {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} + ingressClassName: "{{ tpl .Values.ingress.className . }}" tls: - {{- range .Values.ingress.tls }} - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} + - {{ .Values.global.domainName }} + {{- if .Values.ingress.tls.secretName}} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end}} rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} + - host: {{ .Values.global.domainName }} http: paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} + - pathType: Prefix backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: name: {{ $fullName }} port: number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} + path: /assets/(.*) {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assets/values.yaml b/scripts/helmcharts/openreplay/charts/assets/values.yaml index 2069e0fe8..709f502d1 100644 --- a/scripts/helmcharts/openreplay/charts/assets/values.yaml +++ b/scripts/helmcharts/openreplay/charts/assets/values.yaml @@ -41,20 +41,13 @@ service: port: 9000 ingress: - enabled: false - className: "" + enabled: true + className: "{{ .Values.global.ingress.controller.ingressClassResource.name }}" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + tls: + secretName: openreplay-ssl resources: {} # We usually recommend not to specify default resources and to leave this as a conscious diff --git a/scripts/helmcharts/openreplay/charts/chalice/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/chalice/templates/ingress.yaml index 8f680e949..d870e7673 100644 --- a/scripts/helmcharts/openreplay/charts/chalice/templates/ingress.yaml +++ b/scripts/helmcharts/openreplay/charts/chalice/templates/ingress.yaml @@ -1,61 +1,34 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled }} {{- $fullName := include "chalice.fullname" . -}} {{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} kind: Ingress metadata: name: {{ $fullName }} labels: {{- include "chalice.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$1 + {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} + ingressClassName: "{{ tpl .Values.ingress.className . }}" tls: - {{- range .Values.ingress.tls }} - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} + - {{ .Values.global.domainName }} + {{- if .Values.ingress.tls.secretName}} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end}} rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} + - host: {{ .Values.global.domainName }} http: paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} + - pathType: Prefix backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: name: {{ $fullName }} port: number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} + path: /api/(.*) {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/chalice/values.yaml b/scripts/helmcharts/openreplay/charts/chalice/values.yaml index 3290b9872..a95ed4732 100644 --- a/scripts/helmcharts/openreplay/charts/chalice/values.yaml +++ b/scripts/helmcharts/openreplay/charts/chalice/values.yaml @@ -41,20 +41,13 @@ service: port: 8000 ingress: - enabled: false - className: "" + enabled: true + className: "{{ .Values.global.ingress.controller.ingressClassResource.name }}" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + tls: + secretName: openreplay-ssl resources: {} # We usually recommend not to specify default resources and to leave this as a conscious diff --git a/scripts/helmcharts/openreplay/charts/http/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/http/templates/ingress.yaml index 51a20e2a6..8888c7ae9 100644 --- a/scripts/helmcharts/openreplay/charts/http/templates/ingress.yaml +++ b/scripts/helmcharts/openreplay/charts/http/templates/ingress.yaml @@ -1,61 +1,93 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled }} {{- $fullName := include "http.fullname" . -}} {{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} kind: Ingress metadata: name: {{ $fullName }} labels: {{- include "http.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$1 + nginx.ingress.kubernetes.io/upstream-hash-by: $http_x_forwarded_for + {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} + ingressClassName: "{{ tpl .Values.ingress.className . }}" tls: - {{- range .Values.ingress.tls }} - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} + - {{ .Values.global.domainName }} + {{- if .Values.ingress.tls.secretName}} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end}} rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} + - host: {{ .Values.global.domainName }} http: paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} + - pathType: Prefix backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: name: {{ $fullName }} port: number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} + path: /ingest/(.*) {{- end }} + +## TODO: +## Frontend service from minio will be migrated to nginx atomic container. +## This ingress is just a workaround. +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: minio + namespace: db +spec: + ingressClassName: "{{ tpl .Values.ingress.className . }}" + rules: + - host: {{ .Values.global.domainName }} + http: + paths: + - pathType: Prefix + backend: + service: + name: minio + port: + number: 9000 + path: /(minio|mobs|sessions-assets|frontend|static|sourcemaps|ios-images)/ + tls: + - hosts: + - {{ .Values.global.domainName }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: minio-frontend + namespace: db + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /frontend/$1 + nginx.ingress.kubernetes.io/configuration-snippet: | + index /index.html; + rewrite ^((?!.(js|css|png|svg|jpg|woff|woff2)).)*$ /frontend/index.html break; + proxy_intercept_errors on; # see http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors + error_page 404 =200 /index.html; +spec: + ingressClassName: "{{ tpl .Values.ingress.className . }}" + rules: + - host: {{ .Values.global.domainName }} + http: + paths: + - pathType: Prefix + backend: + service: + name: minio + port: + number: 9000 + path: /(.*) + tls: + - hosts: + - {{ .Values.global.domainName }} + {{- if .Values.ingress.tls.secretName}} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end}} diff --git a/scripts/helmcharts/openreplay/charts/http/values.yaml b/scripts/helmcharts/openreplay/charts/http/values.yaml index 0eb280a12..1f52ed5cf 100644 --- a/scripts/helmcharts/openreplay/charts/http/values.yaml +++ b/scripts/helmcharts/openreplay/charts/http/values.yaml @@ -41,20 +41,17 @@ service: port: 80 ingress: - enabled: false - className: "" - annotations: {} + enabled: true + className: "{{ .Values.global.ingress.controller.ingressClassResource.name }}" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/proxy-connect-timeout: "120" + nginx.ingress.kubernetes.io/proxy-send-timeout: "300" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + tls: + secretName: openreplay-ssl resources: {} # We usually recommend not to specify default resources and to leave this as a conscious diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/.helmignore b/scripts/helmcharts/openreplay/charts/ingress-nginx/.helmignore similarity index 97% rename from scripts/helmcharts/openreplay/charts/nginx-ingress/.helmignore rename to scripts/helmcharts/openreplay/charts/ingress-nginx/.helmignore index 0e8a0eb36..50af03172 100644 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/.helmignore +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/.helmignore @@ -14,7 +14,6 @@ *.swp *.bak *.tmp -*.orig *~ # Various IDEs .project diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/CHANGELOG.md b/scripts/helmcharts/openreplay/charts/ingress-nginx/CHANGELOG.md new file mode 100644 index 000000000..f3f44c336 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/CHANGELOG.md @@ -0,0 +1,375 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 4.0.18 +"[8291](https://github.com/kubernetes/ingress-nginx/pull/8291) remove git tag env from cloud build" +"[8286](https://github.com/kubernetes/ingress-nginx/pull/8286) Fix OpenTelemetry sidecar image build" +"[8277](https://github.com/kubernetes/ingress-nginx/pull/8277) Add OpenSSF Best practices badge" +"[8273](https://github.com/kubernetes/ingress-nginx/pull/8273) Issue#8241" +"[8267](https://github.com/kubernetes/ingress-nginx/pull/8267) Add fsGroup value to admission-webhooks/job-patch charts" +"[8262](https://github.com/kubernetes/ingress-nginx/pull/8262) Updated confusing error" +"[8256](https://github.com/kubernetes/ingress-nginx/pull/8256) fix: deny locations with invalid auth-url annotation" +"[8253](https://github.com/kubernetes/ingress-nginx/pull/8253) Add a certificate info metric" +"[8236](https://github.com/kubernetes/ingress-nginx/pull/8236) webhook: remove useless code." +"[8227](https://github.com/kubernetes/ingress-nginx/pull/8227) Update libraries in webhook image" +"[8225](https://github.com/kubernetes/ingress-nginx/pull/8225) fix inconsistent-label-cardinality for prometheus metrics: nginx_ingress_controller_requests" +"[8221](https://github.com/kubernetes/ingress-nginx/pull/8221) Do not validate ingresses with unknown ingress class in admission webhook endpoint" +"[8210](https://github.com/kubernetes/ingress-nginx/pull/8210) Bump github.com/prometheus/client_golang from 1.11.0 to 1.12.1" +"[8209](https://github.com/kubernetes/ingress-nginx/pull/8209) Bump google.golang.org/grpc from 1.43.0 to 1.44.0" +"[8204](https://github.com/kubernetes/ingress-nginx/pull/8204) Add Artifact Hub lint" +"[8203](https://github.com/kubernetes/ingress-nginx/pull/8203) Fix Indentation of example and link to cert-manager tutorial" +"[8201](https://github.com/kubernetes/ingress-nginx/pull/8201) feat(metrics): add path and method labels to requests countera" +"[8199](https://github.com/kubernetes/ingress-nginx/pull/8199) use functional options to reduce number of methods creating an EchoDeployment" +"[8196](https://github.com/kubernetes/ingress-nginx/pull/8196) docs: fix inconsistent controller annotation" +"[8191](https://github.com/kubernetes/ingress-nginx/pull/8191) Using Go install for misspell" +"[8186](https://github.com/kubernetes/ingress-nginx/pull/8186) prometheus+grafana using servicemonitor" +"[8185](https://github.com/kubernetes/ingress-nginx/pull/8185) Append elements on match, instead of removing for cors-annotations" +"[8179](https://github.com/kubernetes/ingress-nginx/pull/8179) Bump github.com/opencontainers/runc from 1.0.3 to 1.1.0" +"[8173](https://github.com/kubernetes/ingress-nginx/pull/8173) Adding annotations to the controller service account" +"[8163](https://github.com/kubernetes/ingress-nginx/pull/8163) Update the $req_id placeholder description" +"[8162](https://github.com/kubernetes/ingress-nginx/pull/8162) Versioned static manifests" +"[8159](https://github.com/kubernetes/ingress-nginx/pull/8159) Adding some geoip variables and default values" +"[8155](https://github.com/kubernetes/ingress-nginx/pull/8155) #7271 feat: avoid-pdb-creation-when-default-backend-disabled-and-replicas-gt-1" +"[8151](https://github.com/kubernetes/ingress-nginx/pull/8151) Automatically generate helm docs" +"[8143](https://github.com/kubernetes/ingress-nginx/pull/8143) Allow to configure delay before controller exits" +"[8136](https://github.com/kubernetes/ingress-nginx/pull/8136) add ingressClass option to helm chart - back compatibility with ingress.class annotations" +"[8126](https://github.com/kubernetes/ingress-nginx/pull/8126) Example for JWT" + + +### 4.0.15 + +- [8120] https://github.com/kubernetes/ingress-nginx/pull/8120 Update go in runner and release v1.1.1 +- [8119] https://github.com/kubernetes/ingress-nginx/pull/8119 Update to go v1.17.6 +- [8118] https://github.com/kubernetes/ingress-nginx/pull/8118 Remove deprecated libraries, update other libs +- [8117] https://github.com/kubernetes/ingress-nginx/pull/8117 Fix codegen errors +- [8115] https://github.com/kubernetes/ingress-nginx/pull/8115 chart/ghaction: set the correct permission to have access to push a release +- [8098] https://github.com/kubernetes/ingress-nginx/pull/8098 generating SHA for CA only certs in backend_ssl.go + comparision of P… +- [8088] https://github.com/kubernetes/ingress-nginx/pull/8088 Fix Edit this page link to use main branch +- [8072] https://github.com/kubernetes/ingress-nginx/pull/8072 Expose GeoIP2 Continent code as variable +- [8061] https://github.com/kubernetes/ingress-nginx/pull/8061 docs(charts): using helm-docs for chart +- [8058] https://github.com/kubernetes/ingress-nginx/pull/8058 Bump github.com/spf13/cobra from 1.2.1 to 1.3.0 +- [8054] https://github.com/kubernetes/ingress-nginx/pull/8054 Bump google.golang.org/grpc from 1.41.0 to 1.43.0 +- [8051] https://github.com/kubernetes/ingress-nginx/pull/8051 align bug report with feature request regarding kind documentation +- [8046] https://github.com/kubernetes/ingress-nginx/pull/8046 Report expired certificates (#8045) +- [8044] https://github.com/kubernetes/ingress-nginx/pull/8044 remove G109 check till gosec resolves issues +- [8042] https://github.com/kubernetes/ingress-nginx/pull/8042 docs_multiple_instances_one_cluster_ticket_7543 +- [8041] https://github.com/kubernetes/ingress-nginx/pull/8041 docs: fix typo'd executible name +- [8035] https://github.com/kubernetes/ingress-nginx/pull/8035 Comment busy owners +- [8029] https://github.com/kubernetes/ingress-nginx/pull/8029 Add stream-snippet as a ConfigMap and Annotation option +- [8023] https://github.com/kubernetes/ingress-nginx/pull/8023 fix nginx compilation flags +- [8021] https://github.com/kubernetes/ingress-nginx/pull/8021 Disable default modsecurity_rules_file if modsecurity-snippet is specified +- [8019] https://github.com/kubernetes/ingress-nginx/pull/8019 Revise main documentation page +- [8018] https://github.com/kubernetes/ingress-nginx/pull/8018 Preserve order of plugin invocation +- [8015] https://github.com/kubernetes/ingress-nginx/pull/8015 Add newline indenting to admission webhook annotations +- [8014] https://github.com/kubernetes/ingress-nginx/pull/8014 Add link to example error page manifest in docs +- [8009] https://github.com/kubernetes/ingress-nginx/pull/8009 Fix spelling in documentation and top-level files +- [8008] https://github.com/kubernetes/ingress-nginx/pull/8008 Add relabelings in controller-servicemonitor.yaml +- [8003] https://github.com/kubernetes/ingress-nginx/pull/8003 Minor improvements (formatting, consistency) in install guide +- [8001] https://github.com/kubernetes/ingress-nginx/pull/8001 fix: go-grpc Dockerfile +- [7999] https://github.com/kubernetes/ingress-nginx/pull/7999 images: use k8s-staging-test-infra/gcb-docker-gcloud +- [7996] https://github.com/kubernetes/ingress-nginx/pull/7996 doc: improvement +- [7983] https://github.com/kubernetes/ingress-nginx/pull/7983 Fix a couple of misspellings in the annotations documentation. +- [7979] https://github.com/kubernetes/ingress-nginx/pull/7979 allow set annotations for admission Jobs +- [7977] https://github.com/kubernetes/ingress-nginx/pull/7977 Add ssl_reject_handshake to defaul server +- [7975] https://github.com/kubernetes/ingress-nginx/pull/7975 add legacy version update v0.50.0 to main changelog +- [7972] https://github.com/kubernetes/ingress-nginx/pull/7972 updated service upstream definition + +### 4.0.14 + +- [8061] https://github.com/kubernetes/ingress-nginx/pull/8061 Using helm-docs to populate values table in README.md + +### 4.0.13 + +- [8008] https://github.com/kubernetes/ingress-nginx/pull/8008 Add relabelings in controller-servicemonitor.yaml + +### 4.0.12 + +- [7978] https://github.com/kubernetes/ingress-nginx/pull/7979 Support custom annotations in admissions Jobs + +### 4.0.11 + +- [7873] https://github.com/kubernetes/ingress-nginx/pull/7873 Makes the [appProtocol](https://kubernetes.io/docs/concepts/services-networking/_print/#application-protocol) field optional. + +### 4.0.10 + +- [7964] https://github.com/kubernetes/ingress-nginx/pull/7964 Update controller version to v1.1.0 + +### 4.0.9 + +- [6992] https://github.com/kubernetes/ingress-nginx/pull/6992 Add ability to specify labels for all resources + +### 4.0.7 + +- [7923] https://github.com/kubernetes/ingress-nginx/pull/7923 Release v1.0.5 of ingress-nginx +- [7806] https://github.com/kubernetes/ingress-nginx/pull/7806 Choice option for internal/external loadbalancer type service + +### 4.0.6 + +- [7804] https://github.com/kubernetes/ingress-nginx/pull/7804 Release v1.0.4 of ingress-nginx +- [7651] https://github.com/kubernetes/ingress-nginx/pull/7651 Support ipFamilyPolicy and ipFamilies fields in Helm Chart +- [7798] https://github.com/kubernetes/ingress-nginx/pull/7798 Exoscale: use HTTP Healthcheck mode +- [7793] https://github.com/kubernetes/ingress-nginx/pull/7793 Update kube-webhook-certgen to v1.1.1 + +### 4.0.5 + +- [7740] https://github.com/kubernetes/ingress-nginx/pull/7740 Release v1.0.3 of ingress-nginx + +### 4.0.3 + +- [7707] https://github.com/kubernetes/ingress-nginx/pull/7707 Release v1.0.2 of ingress-nginx + +### 4.0.2 + +- [7681] https://github.com/kubernetes/ingress-nginx/pull/7681 Release v1.0.1 of ingress-nginx + +### 4.0.1 + +- [7535] https://github.com/kubernetes/ingress-nginx/pull/7535 Release v1.0.0 ingress-nginx + +### 3.34.0 + +- [7256] https://github.com/kubernetes/ingress-nginx/pull/7256 Add namespace field in the namespace scoped resource templates + +### 3.33.0 + +- [7164] https://github.com/kubernetes/ingress-nginx/pull/7164 Update nginx to v1.20.1 + +### 3.32.0 + +- [7117] https://github.com/kubernetes/ingress-nginx/pull/7117 Add annotations for HPA + +### 3.31.0 + +- [7137] https://github.com/kubernetes/ingress-nginx/pull/7137 Add support for custom probes + +### 3.30.0 + +- [#7092](https://github.com/kubernetes/ingress-nginx/pull/7092) Removes the possibility of using localhost in ExternalNames as endpoints + +### 3.29.0 + +- [X] [#6945](https://github.com/kubernetes/ingress-nginx/pull/7020) Add option to specify job label for ServiceMonitor + +### 3.28.0 + +- [ ] [#6900](https://github.com/kubernetes/ingress-nginx/pull/6900) Support existing PSPs + +### 3.27.0 + +- Update ingress-nginx v0.45.0 + +### 3.26.0 + +- [X] [#6979](https://github.com/kubernetes/ingress-nginx/pull/6979) Changed servicePort value for metrics + +### 3.25.0 + +- [X] [#6957](https://github.com/kubernetes/ingress-nginx/pull/6957) Add ability to specify automountServiceAccountToken + +### 3.24.0 + +- [X] [#6908](https://github.com/kubernetes/ingress-nginx/pull/6908) Add volumes to default-backend deployment + +### 3.23.0 + +- Update ingress-nginx v0.44.0 + +### 3.22.0 + +- [X] [#6802](https://github.com/kubernetes/ingress-nginx/pull/6802) Add value for configuring a custom Diffie-Hellman parameters file +- [X] [#6815](https://github.com/kubernetes/ingress-nginx/pull/6815) Allow use of numeric namespaces in helm chart + +### 3.21.0 + +- [X] [#6783](https://github.com/kubernetes/ingress-nginx/pull/6783) Add custom annotations to ScaledObject +- [X] [#6761](https://github.com/kubernetes/ingress-nginx/pull/6761) Adding quotes in the serviceAccount name in Helm values +- [X] [#6767](https://github.com/kubernetes/ingress-nginx/pull/6767) Remove ClusterRole when scope option is enabled +- [X] [#6785](https://github.com/kubernetes/ingress-nginx/pull/6785) Update kube-webhook-certgen image to v1.5.1 + +### 3.20.1 + +- Do not create KEDA in case of DaemonSets. +- Fix KEDA v2 definition + +### 3.20.0 + +- [X] [#6730](https://github.com/kubernetes/ingress-nginx/pull/6730) Do not create HPA for defaultBackend if not enabled. + +### 3.19.0 + +- Update ingress-nginx v0.43.0 + +### 3.18.0 + +- [X] [#6688](https://github.com/kubernetes/ingress-nginx/pull/6688) Allow volume-type emptyDir in controller podsecuritypolicy +- [X] [#6691](https://github.com/kubernetes/ingress-nginx/pull/6691) Improve parsing of helm parameters + +### 3.17.0 + +- Update ingress-nginx v0.42.0 + +### 3.16.1 + +- Fix chart-releaser action + +### 3.16.0 + +- [X] [#6646](https://github.com/kubernetes/ingress-nginx/pull/6646) Added LoadBalancerIP value for internal service + +### 3.15.1 + +- Fix chart-releaser action + +### 3.15.0 + +- [X] [#6586](https://github.com/kubernetes/ingress-nginx/pull/6586) Fix 'maxmindLicenseKey' location in values.yaml + +### 3.14.0 + +- [X] [#6469](https://github.com/kubernetes/ingress-nginx/pull/6469) Allow custom service names for controller and backend + +### 3.13.0 + +- [X] [#6544](https://github.com/kubernetes/ingress-nginx/pull/6544) Fix default backend HPA name variable + +### 3.12.0 + +- [X] [#6514](https://github.com/kubernetes/ingress-nginx/pull/6514) Remove helm2 support and update docs + +### 3.11.1 + +- [X] [#6505](https://github.com/kubernetes/ingress-nginx/pull/6505) Reorder HPA resource list to work with GitOps tooling + +### 3.11.0 + +- Support Keda Autoscaling + +### 3.10.1 + +- Fix regression introduced in 0.41.0 with external authentication + +### 3.10.0 + +- Fix routing regression introduced in 0.41.0 with PathType Exact + +### 3.9.0 + +- [X] [#6423](https://github.com/kubernetes/ingress-nginx/pull/6423) Add Default backend HPA autoscaling + +### 3.8.0 + +- [X] [#6395](https://github.com/kubernetes/ingress-nginx/pull/6395) Update jettech/kube-webhook-certgen image +- [X] [#6377](https://github.com/kubernetes/ingress-nginx/pull/6377) Added loadBalancerSourceRanges for internal lbs +- [X] [#6356](https://github.com/kubernetes/ingress-nginx/pull/6356) Add securitycontext settings on defaultbackend +- [X] [#6401](https://github.com/kubernetes/ingress-nginx/pull/6401) Fix controller service annotations +- [X] [#6403](https://github.com/kubernetes/ingress-nginx/pull/6403) Initial helm chart changelog + +### 3.7.1 + +- [X] [#6326](https://github.com/kubernetes/ingress-nginx/pull/6326) Fix liveness and readiness probe path in daemonset chart + +### 3.7.0 + +- [X] [#6316](https://github.com/kubernetes/ingress-nginx/pull/6316) Numerals in podAnnotations in quotes [#6315](https://github.com/kubernetes/ingress-nginx/issues/6315) + +### 3.6.0 + +- [X] [#6305](https://github.com/kubernetes/ingress-nginx/pull/6305) Add default linux nodeSelector + +### 3.5.1 + +- [X] [#6299](https://github.com/kubernetes/ingress-nginx/pull/6299) Fix helm chart release + +### 3.5.0 + +- [X] [#6260](https://github.com/kubernetes/ingress-nginx/pull/6260) Allow Helm Chart to customize admission webhook's annotations, timeoutSeconds, namespaceSelector, objectSelector and cert files locations + +### 3.4.0 + +- [X] [#6268](https://github.com/kubernetes/ingress-nginx/pull/6268) Update to 0.40.2 in helm chart #6288 + +### 3.3.1 + +- [X] [#6259](https://github.com/kubernetes/ingress-nginx/pull/6259) Release helm chart +- [X] [#6258](https://github.com/kubernetes/ingress-nginx/pull/6258) Fix chart markdown link +- [X] [#6253](https://github.com/kubernetes/ingress-nginx/pull/6253) Release v0.40.0 + +### 3.3.1 + +- [X] [#6233](https://github.com/kubernetes/ingress-nginx/pull/6233) Add admission controller e2e test + +### 3.3.0 + +- [X] [#6203](https://github.com/kubernetes/ingress-nginx/pull/6203) Refactor parsing of key values +- [X] [#6162](https://github.com/kubernetes/ingress-nginx/pull/6162) Add helm chart options to expose metrics service as NodePort +- [X] [#6180](https://github.com/kubernetes/ingress-nginx/pull/6180) Fix helm chart admissionReviewVersions regression +- [X] [#6169](https://github.com/kubernetes/ingress-nginx/pull/6169) Fix Typo in example prometheus rules + +### 3.0.0 + +- [X] [#6167](https://github.com/kubernetes/ingress-nginx/pull/6167) Update chart requirements + +### 2.16.0 + +- [X] [#6154](https://github.com/kubernetes/ingress-nginx/pull/6154) add `topologySpreadConstraint` to controller + +### 2.15.0 + +- [X] [#6087](https://github.com/kubernetes/ingress-nginx/pull/6087) Adding parameter for externalTrafficPolicy in internal controller service spec + +### 2.14.0 + +- [X] [#6104](https://github.com/kubernetes/ingress-nginx/pull/6104) Misc fixes for nginx-ingress chart for better keel and prometheus-operator integration + +### 2.13.0 + +- [X] [#6093](https://github.com/kubernetes/ingress-nginx/pull/6093) Release v0.35.0 + +### 2.13.0 + +- [X] [#6093](https://github.com/kubernetes/ingress-nginx/pull/6093) Release v0.35.0 +- [X] [#6080](https://github.com/kubernetes/ingress-nginx/pull/6080) Switch images to k8s.gcr.io after Vanity Domain Flip + +### 2.12.1 + +- [X] [#6075](https://github.com/kubernetes/ingress-nginx/pull/6075) Sync helm chart affinity examples + +### 2.12.0 + +- [X] [#6039](https://github.com/kubernetes/ingress-nginx/pull/6039) Add configurable serviceMonitor metricRelabelling and targetLabels +- [X] [#6044](https://github.com/kubernetes/ingress-nginx/pull/6044) Fix YAML linting + +### 2.11.3 + +- [X] [#6038](https://github.com/kubernetes/ingress-nginx/pull/6038) Bump chart version PATCH + +### 2.11.2 + +- [X] [#5951](https://github.com/kubernetes/ingress-nginx/pull/5951) Bump chart patch version + +### 2.11.1 + +- [X] [#5900](https://github.com/kubernetes/ingress-nginx/pull/5900) Release helm chart for v0.34.1 + +### 2.11.0 + +- [X] [#5879](https://github.com/kubernetes/ingress-nginx/pull/5879) Update helm chart for v0.34.0 +- [X] [#5671](https://github.com/kubernetes/ingress-nginx/pull/5671) Make liveness probe more fault tolerant than readiness probe + +### 2.10.0 + +- [X] [#5843](https://github.com/kubernetes/ingress-nginx/pull/5843) Update jettech/kube-webhook-certgen image + +### 2.9.1 + +- [X] [#5823](https://github.com/kubernetes/ingress-nginx/pull/5823) Add quoting to sysctls because numeric values need to be presented as strings (#5823) + +### 2.9.0 + +- [X] [#5795](https://github.com/kubernetes/ingress-nginx/pull/5795) Use fully qualified images to avoid cri-o issues + + +### TODO + +Keep building the changelog using *git log charts* checking the tag diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/Chart.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/Chart.yaml new file mode 100644 index 000000000..6445231d5 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/Chart.yaml @@ -0,0 +1,25 @@ +annotations: + artifacthub.io/changes: | + - "#8253 Add a certificate info metric" + - "#8225 fix inconsistent-label-cardinality for prometheus metrics: nginx_ingress_controller_requests" + - "#8162 Versioned static manifests" + - "#8159 Adding some geoip variables and default values" + - "#8221 Do not validate ingresses with unknown ingress class in admission webhook endpoint" + artifacthub.io/prerelease: "false" +apiVersion: v2 +appVersion: 1.1.2 +description: Ingress controller for Kubernetes using NGINX as a reverse proxy and + load balancer +home: https://github.com/kubernetes/ingress-nginx +icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png +keywords: +- ingress +- nginx +kubeVersion: '>=1.19.0-0' +maintainers: +- name: ChiefAlexander +name: ingress-nginx +sources: +- https://github.com/kubernetes/ingress-nginx +type: application +version: 4.0.18 diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/OWNERS b/scripts/helmcharts/openreplay/charts/ingress-nginx/OWNERS new file mode 100644 index 000000000..6b7e049ca --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/OWNERS @@ -0,0 +1,10 @@ +# See the OWNERS docs: https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md + +approvers: +- ingress-nginx-helm-maintainers + +reviewers: +- ingress-nginx-helm-reviewers + +labels: +- area/helm diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/README.md b/scripts/helmcharts/openreplay/charts/ingress-nginx/README.md new file mode 100644 index 000000000..ad641f726 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/README.md @@ -0,0 +1,486 @@ +# ingress-nginx + +[ingress-nginx](https://github.com/kubernetes/ingress-nginx) Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer + +![Version: 4.0.18](https://img.shields.io/badge/Version-4.0.18-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.1.2](https://img.shields.io/badge/AppVersion-1.1.2-informational?style=flat-square) + +To use, add `ingressClassName: nginx` spec field or the `kubernetes.io/ingress.class: nginx` annotation to your Ingress resources. + +This chart bootstraps an ingress-nginx deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +## Prerequisites + +- Chart version 3.x.x: Kubernetes v1.16+ +- Chart version 4.x.x and above: Kubernetes v1.19+ + +## Get Repo Info + +```console +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx +helm repo update +``` + +## Install Chart + +**Important:** only helm3 is supported + +```console +helm install [RELEASE_NAME] ingress-nginx/ingress-nginx +``` + +The command deploys ingress-nginx on the Kubernetes cluster in the default configuration. + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] [CHART] --install +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading With Zero Downtime in Production + +By default the ingress-nginx controller has service interruptions whenever it's pods are restarted or redeployed. In order to fix that, see the excellent blog post by Lindsay Landry from Codecademy: [Kubernetes: Nginx and Zero Downtime in Production](https://medium.com/codecademy-engineering/kubernetes-nginx-and-zero-downtime-in-production-2c910c6a5ed8). + +### Migrating from stable/nginx-ingress + +There are two main ways to migrate a release from `stable/nginx-ingress` to `ingress-nginx/ingress-nginx` chart: + +1. For Nginx Ingress controllers used for non-critical services, the easiest method is to [uninstall](#uninstall-chart) the old release and [install](#install-chart) the new one +1. For critical services in production that require zero-downtime, you will want to: + 1. [Install](#install-chart) a second Ingress controller + 1. Redirect your DNS traffic from the old controller to the new controller + 1. Log traffic from both controllers during this changeover + 1. [Uninstall](#uninstall-chart) the old controller once traffic has fully drained from it + 1. For details on all of these steps see [Upgrading With Zero Downtime in Production](#upgrading-with-zero-downtime-in-production) + +Note that there are some different and upgraded configurations between the two charts, described by Rimas Mocevicius from JFrog in the "Upgrading to ingress-nginx Helm chart" section of [Migrating from Helm chart nginx-ingress to ingress-nginx](https://rimusz.net/migrating-to-ingress-nginx). As the `ingress-nginx/ingress-nginx` chart continues to update, you will want to check current differences by running [helm configuration](#configuration) commands on both charts. + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments, visit the chart's [values.yaml](./values.yaml), or run these configuration commands: + +```console +helm show values ingress-nginx/ingress-nginx +``` + +### PodDisruptionBudget + +Note that the PodDisruptionBudget resource will only be defined if the replicaCount is greater than one, +else it would make it impossible to evacuate a node. See [gh issue #7127](https://github.com/helm/charts/issues/7127) for more info. + +### Prometheus Metrics + +The Nginx ingress controller can export Prometheus metrics, by setting `controller.metrics.enabled` to `true`. + +You can add Prometheus annotations to the metrics service using `controller.metrics.service.annotations`. +Alternatively, if you use the Prometheus Operator, you can enable ServiceMonitor creation using `controller.metrics.serviceMonitor.enabled`. And set `controller.metrics.serviceMonitor.additionalLabels.release="prometheus"`. "release=prometheus" should match the label configured in the prometheus servicemonitor ( see `kubectl get servicemonitor prometheus-kube-prom-prometheus -oyaml -n prometheus`) + +### ingress-nginx nginx\_status page/stats server + +Previous versions of this chart had a `controller.stats.*` configuration block, which is now obsolete due to the following changes in nginx ingress controller: + +- In [0.16.1](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0161), the vts (virtual host traffic status) dashboard was removed +- In [0.23.0](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0230), the status page at port 18080 is now a unix socket webserver only available at localhost. + You can use `curl --unix-socket /tmp/nginx-status-server.sock http://localhost/nginx_status` inside the controller container to access it locally, or use the snippet from [nginx-ingress changelog](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0230) to re-enable the http server + +### ExternalDNS Service Configuration + +Add an [ExternalDNS](https://github.com/kubernetes-incubator/external-dns) annotation to the LoadBalancer service: + +```yaml +controller: + service: + annotations: + external-dns.alpha.kubernetes.io/hostname: kubernetes-example.com. +``` + +### AWS L7 ELB with SSL Termination + +Annotate the controller as shown in the [nginx-ingress l7 patch](https://github.com/kubernetes/ingress-nginx/blob/main/deploy/aws/l7/service-l7.yaml): + +```yaml +controller: + service: + targetPorts: + http: http + https: http + annotations: + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:XX-XXXX-X:XXXXXXXXX:certificate/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http" + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" + service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '3600' +``` + +### AWS route53-mapper + +To configure the LoadBalancer service with the [route53-mapper addon](https://github.com/kubernetes/kops/tree/master/addons/route53-mapper), add the `domainName` annotation and `dns` label: + +```yaml +controller: + service: + labels: + dns: "route53" + annotations: + domainName: "kubernetes-example.com" +``` + +### Additional Internal Load Balancer + +This setup is useful when you need both external and internal load balancers but don't want to have multiple ingress controllers and multiple ingress objects per application. + +By default, the ingress object will point to the external load balancer address, but if correctly configured, you can make use of the internal one if the URL you are looking up resolves to the internal load balancer's URL. + +You'll need to set both the following values: + +`controller.service.internal.enabled` +`controller.service.internal.annotations` + +If one of them is missing the internal load balancer will not be deployed. Example you may have `controller.service.internal.enabled=true` but no annotations set, in this case no action will be taken. + +`controller.service.internal.annotations` varies with the cloud service you're using. + +Example for AWS: + +```yaml +controller: + service: + internal: + enabled: true + annotations: + # Create internal ELB + service.beta.kubernetes.io/aws-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +Example for GCE: + +```yaml +controller: + service: + internal: + enabled: true + annotations: + # Create internal LB. More informations: https://cloud.google.com/kubernetes-engine/docs/how-to/internal-load-balancing + # For GKE versions 1.17 and later + networking.gke.io/load-balancer-type: "Internal" + # For earlier versions + # cloud.google.com/load-balancer-type: "Internal" + + # Any other annotation can be declared here. +``` + +Example for Azure: + +```yaml +controller: + service: + annotations: + # Create internal LB + service.beta.kubernetes.io/azure-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +Example for Oracle Cloud Infrastructure: + +```yaml +controller: + service: + annotations: + # Create internal LB + service.beta.kubernetes.io/oci-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +An use case for this scenario is having a split-view DNS setup where the public zone CNAME records point to the external balancer URL while the private zone CNAME records point to the internal balancer URL. This way, you only need one ingress kubernetes object. + +Optionally you can set `controller.service.loadBalancerIP` if you need a static IP for the resulting `LoadBalancer`. + +### Ingress Admission Webhooks + +With nginx-ingress-controller version 0.25+, the nginx ingress controller pod exposes an endpoint that will integrate with the `validatingwebhookconfiguration` Kubernetes feature to prevent bad ingress from being added to the cluster. +**This feature is enabled by default since 0.31.0.** + +With nginx-ingress-controller in 0.25.* work only with kubernetes 1.14+, 0.26 fix [this issue](https://github.com/kubernetes/ingress-nginx/pull/4521) + +### Helm Error When Upgrading: spec.clusterIP: Invalid value: "" + +If you are upgrading this chart from a version between 0.31.0 and 1.2.2 then you may get an error like this: + +```console +Error: UPGRADE FAILED: Service "?????-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable +``` + +Detail of how and why are in [this issue](https://github.com/helm/charts/pull/13646) but to resolve this you can set `xxxx.service.omitClusterIP` to `true` where `xxxx` is the service referenced in the error. + +As of version `1.26.0` of this chart, by simply not providing any clusterIP value, `invalid: spec.clusterIP: Invalid value: "": field is immutable` will no longer occur since `clusterIP: ""` will not be rendered. + +## Requirements + +Kubernetes: `>=1.19.0-0` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| commonLabels | object | `{}` | | +| controller.addHeaders | object | `{}` | Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headers | +| controller.admissionWebhooks.annotations | object | `{}` | | +| controller.admissionWebhooks.certificate | string | `"/usr/local/certificates/cert"` | | +| controller.admissionWebhooks.createSecretJob.resources | object | `{}` | | +| controller.admissionWebhooks.enabled | bool | `true` | | +| controller.admissionWebhooks.existingPsp | string | `""` | Use an existing PSP instead of creating one | +| controller.admissionWebhooks.failurePolicy | string | `"Fail"` | | +| controller.admissionWebhooks.key | string | `"/usr/local/certificates/key"` | | +| controller.admissionWebhooks.labels | object | `{}` | Labels to be added to admission webhooks | +| controller.admissionWebhooks.namespaceSelector | object | `{}` | | +| controller.admissionWebhooks.objectSelector | object | `{}` | | +| controller.admissionWebhooks.patch.enabled | bool | `true` | | +| controller.admissionWebhooks.patch.fsGroup | int | `2000` | | +| controller.admissionWebhooks.patch.image.digest | string | `"sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660"` | | +| controller.admissionWebhooks.patch.image.image | string | `"ingress-nginx/kube-webhook-certgen"` | | +| controller.admissionWebhooks.patch.image.pullPolicy | string | `"IfNotPresent"` | | +| controller.admissionWebhooks.patch.image.registry | string | `"k8s.gcr.io"` | | +| controller.admissionWebhooks.patch.image.tag | string | `"v1.1.1"` | | +| controller.admissionWebhooks.patch.labels | object | `{}` | Labels to be added to patch job resources | +| controller.admissionWebhooks.patch.nodeSelector."kubernetes.io/os" | string | `"linux"` | | +| controller.admissionWebhooks.patch.podAnnotations | object | `{}` | | +| controller.admissionWebhooks.patch.priorityClassName | string | `""` | Provide a priority class name to the webhook patching job | +| controller.admissionWebhooks.patch.runAsUser | int | `2000` | | +| controller.admissionWebhooks.patch.tolerations | list | `[]` | | +| controller.admissionWebhooks.patchWebhookJob.resources | object | `{}` | | +| controller.admissionWebhooks.port | int | `8443` | | +| controller.admissionWebhooks.service.annotations | object | `{}` | | +| controller.admissionWebhooks.service.externalIPs | list | `[]` | | +| controller.admissionWebhooks.service.loadBalancerSourceRanges | list | `[]` | | +| controller.admissionWebhooks.service.servicePort | int | `443` | | +| controller.admissionWebhooks.service.type | string | `"ClusterIP"` | | +| controller.affinity | object | `{}` | Affinity and anti-affinity rules for server scheduling to nodes | +| controller.allowSnippetAnnotations | bool | `true` | This configuration defines if Ingress Controller should allow users to set their own *-snippet annotations, otherwise this is forbidden / dropped when users add those annotations. Global snippets in ConfigMap are still respected | +| controller.annotations | object | `{}` | Annotations to be added to the controller Deployment or DaemonSet | +| controller.autoscaling.behavior | object | `{}` | | +| controller.autoscaling.enabled | bool | `false` | | +| controller.autoscaling.maxReplicas | int | `11` | | +| controller.autoscaling.minReplicas | int | `1` | | +| controller.autoscaling.targetCPUUtilizationPercentage | int | `50` | | +| controller.autoscaling.targetMemoryUtilizationPercentage | int | `50` | | +| controller.autoscalingTemplate | list | `[]` | | +| controller.config | object | `{}` | Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/ | +| controller.configAnnotations | object | `{}` | Annotations to be added to the controller config configuration configmap. | +| controller.configMapNamespace | string | `""` | Allows customization of the configmap / nginx-configmap namespace; defaults to $(POD_NAMESPACE) | +| controller.containerName | string | `"controller"` | Configures the controller container name | +| controller.containerPort | object | `{"http":80,"https":443}` | Configures the ports that the nginx-controller listens on | +| controller.customTemplate.configMapKey | string | `""` | | +| controller.customTemplate.configMapName | string | `""` | | +| controller.dnsConfig | object | `{}` | Optionally customize the pod dnsConfig. | +| controller.dnsPolicy | string | `"ClusterFirst"` | Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'. By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller to keep resolving names inside the k8s network, use ClusterFirstWithHostNet. | +| controller.electionID | string | `"ingress-controller-leader"` | Election ID to use for status update | +| controller.enableMimalloc | bool | `true` | Enable mimalloc as a drop-in replacement for malloc. | +| controller.existingPsp | string | `""` | Use an existing PSP instead of creating one | +| controller.extraArgs | object | `{}` | Additional command line arguments to pass to nginx-ingress-controller E.g. to specify the default SSL certificate you can use | +| controller.extraContainers | list | `[]` | Additional containers to be added to the controller pod. See https://github.com/lemonldap-ng-controller/lemonldap-ng-controller as example. | +| controller.extraEnvs | list | `[]` | Additional environment variables to set | +| controller.extraInitContainers | list | `[]` | Containers, which are run before the app containers are started. | +| controller.extraModules | list | `[]` | | +| controller.extraVolumeMounts | list | `[]` | Additional volumeMounts to the controller main container. | +| controller.extraVolumes | list | `[]` | Additional volumes to the controller pod. | +| controller.healthCheckHost | string | `""` | Address to bind the health check endpoint. It is better to set this option to the internal node address if the ingress nginx controller is running in the `hostNetwork: true` mode. | +| controller.healthCheckPath | string | `"/healthz"` | Path of the health check endpoint. All requests received on the port defined by the healthz-port parameter are forwarded internally to this path. | +| controller.hostNetwork | bool | `false` | Required for use with CNI based kubernetes installations (such as ones set up by kubeadm), since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920 is merged | +| controller.hostPort.enabled | bool | `false` | Enable 'hostPort' or not | +| controller.hostPort.ports.http | int | `80` | 'hostPort' http port | +| controller.hostPort.ports.https | int | `443` | 'hostPort' https port | +| controller.hostname | object | `{}` | Optionally customize the pod hostname. | +| controller.image.allowPrivilegeEscalation | bool | `true` | | +| controller.image.digest | string | `"sha256:28b11ce69e57843de44e3db6413e98d09de0f6688e33d4bd384002a44f78405c"` | | +| controller.image.image | string | `"ingress-nginx/controller"` | | +| controller.image.pullPolicy | string | `"IfNotPresent"` | | +| controller.image.registry | string | `"k8s.gcr.io"` | | +| controller.image.runAsUser | int | `101` | | +| controller.image.tag | string | `"v1.1.2"` | | +| controller.ingressClass | string | `"nginx"` | For backwards compatibility with ingress.class annotation, use ingressClass. Algorithm is as follows, first ingressClassName is considered, if not present, controller looks for ingress.class annotation | +| controller.ingressClassByName | bool | `false` | Process IngressClass per name (additionally as per spec.controller). | +| controller.ingressClassResource.controllerValue | string | `"k8s.io/ingress-nginx"` | Controller-value of the controller that is processing this ingressClass | +| controller.ingressClassResource.default | bool | `false` | Is this the default ingressClass for the cluster | +| controller.ingressClassResource.enabled | bool | `true` | Is this ingressClass enabled or not | +| controller.ingressClassResource.name | string | `"nginx"` | Name of the ingressClass | +| controller.ingressClassResource.parameters | object | `{}` | Parameters is a link to a custom resource containing additional configuration for the controller. This is optional if the controller does not require extra parameters. | +| controller.keda.apiVersion | string | `"keda.sh/v1alpha1"` | | +| controller.keda.behavior | object | `{}` | | +| controller.keda.cooldownPeriod | int | `300` | | +| controller.keda.enabled | bool | `false` | | +| controller.keda.maxReplicas | int | `11` | | +| controller.keda.minReplicas | int | `1` | | +| controller.keda.pollingInterval | int | `30` | | +| controller.keda.restoreToOriginalReplicaCount | bool | `false` | | +| controller.keda.scaledObject.annotations | object | `{}` | | +| controller.keda.triggers | list | `[]` | | +| controller.kind | string | `"Deployment"` | Use a `DaemonSet` or `Deployment` | +| controller.labels | object | `{}` | Labels to be added to the controller Deployment or DaemonSet and other resources that do not have option to specify labels | +| controller.lifecycle | object | `{"preStop":{"exec":{"command":["/wait-shutdown"]}}}` | Improve connection draining when ingress controller pod is deleted using a lifecycle hook: With this new hook, we increased the default terminationGracePeriodSeconds from 30 seconds to 300, allowing the draining of connections up to five minutes. If the active connections end before that, the pod will terminate gracefully at that time. To effectively take advantage of this feature, the Configmap feature worker-shutdown-timeout new value is 240s instead of 10s. | +| controller.livenessProbe.failureThreshold | int | `5` | | +| controller.livenessProbe.httpGet.path | string | `"/healthz"` | | +| controller.livenessProbe.httpGet.port | int | `10254` | | +| controller.livenessProbe.httpGet.scheme | string | `"HTTP"` | | +| controller.livenessProbe.initialDelaySeconds | int | `10` | | +| controller.livenessProbe.periodSeconds | int | `10` | | +| controller.livenessProbe.successThreshold | int | `1` | | +| controller.livenessProbe.timeoutSeconds | int | `1` | | +| controller.maxmindLicenseKey | string | `""` | Maxmind license key to download GeoLite2 Databases. | +| controller.metrics.enabled | bool | `false` | | +| controller.metrics.port | int | `10254` | | +| controller.metrics.prometheusRule.additionalLabels | object | `{}` | | +| controller.metrics.prometheusRule.enabled | bool | `false` | | +| controller.metrics.prometheusRule.rules | list | `[]` | | +| controller.metrics.service.annotations | object | `{}` | | +| controller.metrics.service.externalIPs | list | `[]` | List of IP addresses at which the stats-exporter service is available | +| controller.metrics.service.loadBalancerSourceRanges | list | `[]` | | +| controller.metrics.service.servicePort | int | `10254` | | +| controller.metrics.service.type | string | `"ClusterIP"` | | +| controller.metrics.serviceMonitor.additionalLabels | object | `{}` | | +| controller.metrics.serviceMonitor.enabled | bool | `false` | | +| controller.metrics.serviceMonitor.metricRelabelings | list | `[]` | | +| controller.metrics.serviceMonitor.namespace | string | `""` | | +| controller.metrics.serviceMonitor.namespaceSelector | object | `{}` | | +| controller.metrics.serviceMonitor.relabelings | list | `[]` | | +| controller.metrics.serviceMonitor.scrapeInterval | string | `"30s"` | | +| controller.metrics.serviceMonitor.targetLabels | list | `[]` | | +| controller.minAvailable | int | `1` | | +| controller.minReadySeconds | int | `0` | `minReadySeconds` to avoid killing pods before we are ready | +| controller.name | string | `"controller"` | | +| controller.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for controller pod assignment | +| controller.podAnnotations | object | `{}` | Annotations to be added to controller pods | +| controller.podLabels | object | `{}` | Labels to add to the pod container metadata | +| controller.podSecurityContext | object | `{}` | Security Context policies for controller pods | +| controller.priorityClassName | string | `""` | | +| controller.proxySetHeaders | object | `{}` | Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/main/docs/examples/customization/custom-headers | +| controller.publishService | object | `{"enabled":true,"pathOverride":""}` | Allows customization of the source of the IP address or FQDN to report in the ingress status field. By default, it reads the information provided by the service. If disable, the status field reports the IP address of the node or nodes where an ingress controller pod is running. | +| controller.publishService.enabled | bool | `true` | Enable 'publishService' or not | +| controller.publishService.pathOverride | string | `""` | Allows overriding of the publish service to bind to Must be / | +| controller.readinessProbe.failureThreshold | int | `3` | | +| controller.readinessProbe.httpGet.path | string | `"/healthz"` | | +| controller.readinessProbe.httpGet.port | int | `10254` | | +| controller.readinessProbe.httpGet.scheme | string | `"HTTP"` | | +| controller.readinessProbe.initialDelaySeconds | int | `10` | | +| controller.readinessProbe.periodSeconds | int | `10` | | +| controller.readinessProbe.successThreshold | int | `1` | | +| controller.readinessProbe.timeoutSeconds | int | `1` | | +| controller.replicaCount | int | `1` | | +| controller.reportNodeInternalIp | bool | `false` | Bare-metal considerations via the host network https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network Ingress status was blank because there is no Service exposing the NGINX Ingress controller in a configuration using the host network, the default --publish-service flag used in standard cloud setups does not apply | +| controller.resources.requests.cpu | string | `"100m"` | | +| controller.resources.requests.memory | string | `"90Mi"` | | +| controller.scope.enabled | bool | `false` | Enable 'scope' or not | +| controller.scope.namespace | string | `""` | Namespace to limit the controller to; defaults to $(POD_NAMESPACE) | +| controller.scope.namespaceSelector | string | `""` | When scope.enabled == false, instead of watching all namespaces, we watching namespaces whose labels only match with namespaceSelector. Format like foo=bar. Defaults to empty, means watching all namespaces. | +| controller.service.annotations | object | `{}` | | +| controller.service.appProtocol | bool | `true` | If enabled is adding an appProtocol option for Kubernetes service. An appProtocol field replacing annotations that were using for setting a backend protocol. Here is an example for AWS: service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http It allows choosing the protocol for each backend specified in the Kubernetes service. See the following GitHub issue for more details about the purpose: https://github.com/kubernetes/kubernetes/issues/40244 Will be ignored for Kubernetes versions older than 1.20 | +| controller.service.enableHttp | bool | `true` | | +| controller.service.enableHttps | bool | `true` | | +| controller.service.enabled | bool | `true` | | +| controller.service.external.enabled | bool | `true` | | +| controller.service.externalIPs | list | `[]` | List of IP addresses at which the controller services are available | +| controller.service.internal.annotations | object | `{}` | Annotations are mandatory for the load balancer to come up. Varies with the cloud service. | +| controller.service.internal.enabled | bool | `false` | Enables an additional internal load balancer (besides the external one). | +| controller.service.internal.loadBalancerSourceRanges | list | `[]` | Restrict access For LoadBalancer service. Defaults to 0.0.0.0/0. | +| controller.service.ipFamilies | list | `["IPv4"]` | List of IP families (e.g. IPv4, IPv6) assigned to the service. This field is usually assigned automatically based on cluster configuration and the ipFamilyPolicy field. | +| controller.service.ipFamilyPolicy | string | `"SingleStack"` | Represents the dual-stack-ness requested or required by this Service. Possible values are SingleStack, PreferDualStack or RequireDualStack. The ipFamilies and clusterIPs fields depend on the value of this field. | +| controller.service.labels | object | `{}` | | +| controller.service.loadBalancerSourceRanges | list | `[]` | | +| controller.service.nodePorts.http | string | `""` | | +| controller.service.nodePorts.https | string | `""` | | +| controller.service.nodePorts.tcp | object | `{}` | | +| controller.service.nodePorts.udp | object | `{}` | | +| controller.service.ports.http | int | `80` | | +| controller.service.ports.https | int | `443` | | +| controller.service.targetPorts.http | string | `"http"` | | +| controller.service.targetPorts.https | string | `"https"` | | +| controller.service.type | string | `"LoadBalancer"` | | +| controller.sysctls | object | `{}` | See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for notes on enabling and using sysctls | +| controller.tcp.annotations | object | `{}` | Annotations to be added to the tcp config configmap | +| controller.tcp.configMapNamespace | string | `""` | Allows customization of the tcp-services-configmap; defaults to $(POD_NAMESPACE) | +| controller.terminationGracePeriodSeconds | int | `300` | `terminationGracePeriodSeconds` to avoid killing pods before we are ready | +| controller.tolerations | list | `[]` | Node tolerations for server scheduling to nodes with taints | +| controller.topologySpreadConstraints | list | `[]` | Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in. | +| controller.udp.annotations | object | `{}` | Annotations to be added to the udp config configmap | +| controller.udp.configMapNamespace | string | `""` | Allows customization of the udp-services-configmap; defaults to $(POD_NAMESPACE) | +| controller.updateStrategy | object | `{}` | The update strategy to apply to the Deployment or DaemonSet | +| controller.watchIngressWithoutClass | bool | `false` | Process Ingress objects without ingressClass annotation/ingressClassName field Overrides value for --watch-ingress-without-class flag of the controller binary Defaults to false | +| defaultBackend.affinity | object | `{}` | | +| defaultBackend.autoscaling.annotations | object | `{}` | | +| defaultBackend.autoscaling.enabled | bool | `false` | | +| defaultBackend.autoscaling.maxReplicas | int | `2` | | +| defaultBackend.autoscaling.minReplicas | int | `1` | | +| defaultBackend.autoscaling.targetCPUUtilizationPercentage | int | `50` | | +| defaultBackend.autoscaling.targetMemoryUtilizationPercentage | int | `50` | | +| defaultBackend.containerSecurityContext | object | `{}` | Security Context policies for controller main container. See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for notes on enabling and using sysctls | +| defaultBackend.enabled | bool | `false` | | +| defaultBackend.existingPsp | string | `""` | Use an existing PSP instead of creating one | +| defaultBackend.extraArgs | object | `{}` | | +| defaultBackend.extraEnvs | list | `[]` | Additional environment variables to set for defaultBackend pods | +| defaultBackend.extraVolumeMounts | list | `[]` | | +| defaultBackend.extraVolumes | list | `[]` | | +| defaultBackend.image.allowPrivilegeEscalation | bool | `false` | | +| defaultBackend.image.image | string | `"defaultbackend-amd64"` | | +| defaultBackend.image.pullPolicy | string | `"IfNotPresent"` | | +| defaultBackend.image.readOnlyRootFilesystem | bool | `true` | | +| defaultBackend.image.registry | string | `"k8s.gcr.io"` | | +| defaultBackend.image.runAsNonRoot | bool | `true` | | +| defaultBackend.image.runAsUser | int | `65534` | | +| defaultBackend.image.tag | string | `"1.5"` | | +| defaultBackend.labels | object | `{}` | Labels to be added to the default backend resources | +| defaultBackend.livenessProbe.failureThreshold | int | `3` | | +| defaultBackend.livenessProbe.initialDelaySeconds | int | `30` | | +| defaultBackend.livenessProbe.periodSeconds | int | `10` | | +| defaultBackend.livenessProbe.successThreshold | int | `1` | | +| defaultBackend.livenessProbe.timeoutSeconds | int | `5` | | +| defaultBackend.minAvailable | int | `1` | | +| defaultBackend.name | string | `"defaultbackend"` | | +| defaultBackend.nodeSelector | object | `{"kubernetes.io/os":"linux"}` | Node labels for default backend pod assignment | +| defaultBackend.podAnnotations | object | `{}` | Annotations to be added to default backend pods | +| defaultBackend.podLabels | object | `{}` | Labels to add to the pod container metadata | +| defaultBackend.podSecurityContext | object | `{}` | Security Context policies for controller pods See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for notes on enabling and using sysctls | +| defaultBackend.port | int | `8080` | | +| defaultBackend.priorityClassName | string | `""` | | +| defaultBackend.readinessProbe.failureThreshold | int | `6` | | +| defaultBackend.readinessProbe.initialDelaySeconds | int | `0` | | +| defaultBackend.readinessProbe.periodSeconds | int | `5` | | +| defaultBackend.readinessProbe.successThreshold | int | `1` | | +| defaultBackend.readinessProbe.timeoutSeconds | int | `5` | | +| defaultBackend.replicaCount | int | `1` | | +| defaultBackend.resources | object | `{}` | | +| defaultBackend.service.annotations | object | `{}` | | +| defaultBackend.service.externalIPs | list | `[]` | List of IP addresses at which the default backend service is available | +| defaultBackend.service.loadBalancerSourceRanges | list | `[]` | | +| defaultBackend.service.servicePort | int | `80` | | +| defaultBackend.service.type | string | `"ClusterIP"` | | +| defaultBackend.serviceAccount.automountServiceAccountToken | bool | `true` | | +| defaultBackend.serviceAccount.create | bool | `true` | | +| defaultBackend.serviceAccount.name | string | `""` | | +| defaultBackend.tolerations | list | `[]` | Node tolerations for server scheduling to nodes with taints | +| dhParam | string | `nil` | A base64-encoded Diffie-Hellman parameter. This can be generated with: `openssl dhparam 4096 2> /dev/null | base64` | +| imagePullSecrets | list | `[]` | Optional array of imagePullSecrets containing private registry credentials | +| podSecurityPolicy.enabled | bool | `false` | | +| rbac.create | bool | `true` | | +| rbac.scope | bool | `false` | | +| revisionHistoryLimit | int | `10` | Rollback limit | +| serviceAccount.annotations | object | `{}` | Annotations for the controller service account | +| serviceAccount.automountServiceAccountToken | bool | `true` | | +| serviceAccount.create | bool | `true` | | +| serviceAccount.name | string | `""` | | +| tcp | object | `{}` | TCP service key:value pairs | +| udp | object | `{}` | UDP service key:value pairs | + diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/README.md.gotmpl b/scripts/helmcharts/openreplay/charts/ingress-nginx/README.md.gotmpl new file mode 100644 index 000000000..5cd9e59e1 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/README.md.gotmpl @@ -0,0 +1,235 @@ +{{ template "chart.header" . }} +[ingress-nginx](https://github.com/kubernetes/ingress-nginx) Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer + +{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} + +To use, add `ingressClassName: nginx` spec field or the `kubernetes.io/ingress.class: nginx` annotation to your Ingress resources. + +This chart bootstraps an ingress-nginx deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +## Prerequisites + +- Chart version 3.x.x: Kubernetes v1.16+ +- Chart version 4.x.x and above: Kubernetes v1.19+ + +## Get Repo Info + +```console +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx +helm repo update +``` + +## Install Chart + +**Important:** only helm3 is supported + +```console +helm install [RELEASE_NAME] ingress-nginx/ingress-nginx +``` + +The command deploys ingress-nginx on the Kubernetes cluster in the default configuration. + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] [CHART] --install +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading With Zero Downtime in Production + +By default the ingress-nginx controller has service interruptions whenever it's pods are restarted or redeployed. In order to fix that, see the excellent blog post by Lindsay Landry from Codecademy: [Kubernetes: Nginx and Zero Downtime in Production](https://medium.com/codecademy-engineering/kubernetes-nginx-and-zero-downtime-in-production-2c910c6a5ed8). + +### Migrating from stable/nginx-ingress + +There are two main ways to migrate a release from `stable/nginx-ingress` to `ingress-nginx/ingress-nginx` chart: + +1. For Nginx Ingress controllers used for non-critical services, the easiest method is to [uninstall](#uninstall-chart) the old release and [install](#install-chart) the new one +1. For critical services in production that require zero-downtime, you will want to: + 1. [Install](#install-chart) a second Ingress controller + 1. Redirect your DNS traffic from the old controller to the new controller + 1. Log traffic from both controllers during this changeover + 1. [Uninstall](#uninstall-chart) the old controller once traffic has fully drained from it + 1. For details on all of these steps see [Upgrading With Zero Downtime in Production](#upgrading-with-zero-downtime-in-production) + +Note that there are some different and upgraded configurations between the two charts, described by Rimas Mocevicius from JFrog in the "Upgrading to ingress-nginx Helm chart" section of [Migrating from Helm chart nginx-ingress to ingress-nginx](https://rimusz.net/migrating-to-ingress-nginx). As the `ingress-nginx/ingress-nginx` chart continues to update, you will want to check current differences by running [helm configuration](#configuration) commands on both charts. + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments, visit the chart's [values.yaml](./values.yaml), or run these configuration commands: + +```console +helm show values ingress-nginx/ingress-nginx +``` + +### PodDisruptionBudget + +Note that the PodDisruptionBudget resource will only be defined if the replicaCount is greater than one, +else it would make it impossible to evacuate a node. See [gh issue #7127](https://github.com/helm/charts/issues/7127) for more info. + +### Prometheus Metrics + +The Nginx ingress controller can export Prometheus metrics, by setting `controller.metrics.enabled` to `true`. + +You can add Prometheus annotations to the metrics service using `controller.metrics.service.annotations`. +Alternatively, if you use the Prometheus Operator, you can enable ServiceMonitor creation using `controller.metrics.serviceMonitor.enabled`. And set `controller.metrics.serviceMonitor.additionalLabels.release="prometheus"`. "release=prometheus" should match the label configured in the prometheus servicemonitor ( see `kubectl get servicemonitor prometheus-kube-prom-prometheus -oyaml -n prometheus`) + +### ingress-nginx nginx\_status page/stats server + +Previous versions of this chart had a `controller.stats.*` configuration block, which is now obsolete due to the following changes in nginx ingress controller: + +- In [0.16.1](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0161), the vts (virtual host traffic status) dashboard was removed +- In [0.23.0](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0230), the status page at port 18080 is now a unix socket webserver only available at localhost. + You can use `curl --unix-socket /tmp/nginx-status-server.sock http://localhost/nginx_status` inside the controller container to access it locally, or use the snippet from [nginx-ingress changelog](https://github.com/kubernetes/ingress-nginx/blob/main/Changelog.md#0230) to re-enable the http server + +### ExternalDNS Service Configuration + +Add an [ExternalDNS](https://github.com/kubernetes-incubator/external-dns) annotation to the LoadBalancer service: + +```yaml +controller: + service: + annotations: + external-dns.alpha.kubernetes.io/hostname: kubernetes-example.com. +``` + +### AWS L7 ELB with SSL Termination + +Annotate the controller as shown in the [nginx-ingress l7 patch](https://github.com/kubernetes/ingress-nginx/blob/main/deploy/aws/l7/service-l7.yaml): + +```yaml +controller: + service: + targetPorts: + http: http + https: http + annotations: + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:XX-XXXX-X:XXXXXXXXX:certificate/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http" + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" + service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '3600' +``` + +### AWS route53-mapper + +To configure the LoadBalancer service with the [route53-mapper addon](https://github.com/kubernetes/kops/tree/master/addons/route53-mapper), add the `domainName` annotation and `dns` label: + +```yaml +controller: + service: + labels: + dns: "route53" + annotations: + domainName: "kubernetes-example.com" +``` + +### Additional Internal Load Balancer + +This setup is useful when you need both external and internal load balancers but don't want to have multiple ingress controllers and multiple ingress objects per application. + +By default, the ingress object will point to the external load balancer address, but if correctly configured, you can make use of the internal one if the URL you are looking up resolves to the internal load balancer's URL. + +You'll need to set both the following values: + +`controller.service.internal.enabled` +`controller.service.internal.annotations` + +If one of them is missing the internal load balancer will not be deployed. Example you may have `controller.service.internal.enabled=true` but no annotations set, in this case no action will be taken. + +`controller.service.internal.annotations` varies with the cloud service you're using. + +Example for AWS: + +```yaml +controller: + service: + internal: + enabled: true + annotations: + # Create internal ELB + service.beta.kubernetes.io/aws-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +Example for GCE: + +```yaml +controller: + service: + internal: + enabled: true + annotations: + # Create internal LB. More informations: https://cloud.google.com/kubernetes-engine/docs/how-to/internal-load-balancing + # For GKE versions 1.17 and later + networking.gke.io/load-balancer-type: "Internal" + # For earlier versions + # cloud.google.com/load-balancer-type: "Internal" + + # Any other annotation can be declared here. +``` + +Example for Azure: + +```yaml +controller: + service: + annotations: + # Create internal LB + service.beta.kubernetes.io/azure-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +Example for Oracle Cloud Infrastructure: + +```yaml +controller: + service: + annotations: + # Create internal LB + service.beta.kubernetes.io/oci-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +An use case for this scenario is having a split-view DNS setup where the public zone CNAME records point to the external balancer URL while the private zone CNAME records point to the internal balancer URL. This way, you only need one ingress kubernetes object. + +Optionally you can set `controller.service.loadBalancerIP` if you need a static IP for the resulting `LoadBalancer`. + +### Ingress Admission Webhooks + +With nginx-ingress-controller version 0.25+, the nginx ingress controller pod exposes an endpoint that will integrate with the `validatingwebhookconfiguration` Kubernetes feature to prevent bad ingress from being added to the cluster. +**This feature is enabled by default since 0.31.0.** + +With nginx-ingress-controller in 0.25.* work only with kubernetes 1.14+, 0.26 fix [this issue](https://github.com/kubernetes/ingress-nginx/pull/4521) + +### Helm Error When Upgrading: spec.clusterIP: Invalid value: "" + +If you are upgrading this chart from a version between 0.31.0 and 1.2.2 then you may get an error like this: + +```console +Error: UPGRADE FAILED: Service "?????-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable +``` + +Detail of how and why are in [this issue](https://github.com/helm/charts/pull/13646) but to resolve this you can set `xxxx.service.omitClusterIP` to `true` where `xxxx` is the service referenced in the error. + +As of version `1.26.0` of this chart, by simply not providing any clusterIP value, `invalid: spec.clusterIP: Invalid value: "": field is immutable` will no longer occur since `clusterIP: ""` will not be rendered. + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/controller-custom-ingressclass-flags.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/controller-custom-ingressclass-flags.yaml new file mode 100644 index 000000000..b28a2326e --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/controller-custom-ingressclass-flags.yaml @@ -0,0 +1,7 @@ +controller: + watchIngressWithoutClass: true + ingressClassResource: + name: custom-nginx + enabled: true + default: true + controllerValue: "k8s.io/custom-nginx" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml new file mode 100644 index 000000000..4393a5bc0 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml @@ -0,0 +1,14 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + kind: DaemonSet + allowSnippetAnnotations: false + admissionWebhooks: + enabled: false + service: + type: ClusterIP + + config: + use-proxy-protocol: "true" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml new file mode 100644 index 000000000..1d94be219 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml @@ -0,0 +1,22 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + + service: + type: NodePort + nodePorts: + tcp: + 9000: 30090 + udp: + 9001: 30091 + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-extra-modules.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-extra-modules.yaml new file mode 100644 index 000000000..f299dbf1c --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-extra-modules.yaml @@ -0,0 +1,10 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + service: + type: ClusterIP + extraModules: + - name: opentelemetry + image: busybox diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-headers-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-headers-values.yaml new file mode 100644 index 000000000..ab7d47bd4 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-headers-values.yaml @@ -0,0 +1,14 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + addHeaders: + X-Frame-Options: deny + proxySetHeaders: + X-Forwarded-Proto: https + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml new file mode 100644 index 000000000..0a200a746 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml @@ -0,0 +1,14 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP + internal: + enabled: true + annotations: + service.beta.kubernetes.io/aws-load-balancer-internal: "true" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml new file mode 100644 index 000000000..3b7aa2fcd --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml @@ -0,0 +1,10 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: NodePort diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml new file mode 100644 index 000000000..0b55306a1 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml @@ -0,0 +1,17 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + metrics: + enabled: true + service: + type: ClusterIP + podAnnotations: + prometheus.io/path: /metrics + prometheus.io/port: "10254" + prometheus.io/scheme: http + prometheus.io/scrape: "true" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml new file mode 100644 index 000000000..acd86a77a --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml @@ -0,0 +1,20 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP + tcp: + configMapNamespace: default + udp: + configMapNamespace: default + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml new file mode 100644 index 000000000..25ee64d85 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml @@ -0,0 +1,16 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-values.yaml new file mode 100644 index 000000000..380c8b4b1 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/daemonset-tcp-values.yaml @@ -0,0 +1,14 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP + +tcp: + 9000: "default/test:8080" + 9001: "default/test:8080" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-default-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-default-values.yaml new file mode 100644 index 000000000..82fa23e85 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-default-values.yaml @@ -0,0 +1,10 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-metrics-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-metrics-values.yaml new file mode 100644 index 000000000..cb3cb54be --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-metrics-values.yaml @@ -0,0 +1,12 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + metrics: + enabled: true + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-psp-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-psp-values.yaml new file mode 100644 index 000000000..8026a6356 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-psp-values.yaml @@ -0,0 +1,13 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP + +podSecurityPolicy: + enabled: true diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml new file mode 100644 index 000000000..fccdb134c --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml @@ -0,0 +1,13 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: true + service: + type: ClusterIP + +podSecurityPolicy: + enabled: true diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-webhook-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-webhook-values.yaml new file mode 100644 index 000000000..54d364df1 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deamonset-webhook-values.yaml @@ -0,0 +1,10 @@ +controller: + kind: DaemonSet + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: true + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-autoscaling-behavior-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-autoscaling-behavior-values.yaml new file mode 100644 index 000000000..dca3f35f8 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-autoscaling-behavior-values.yaml @@ -0,0 +1,14 @@ +controller: + autoscaling: + enabled: true + behavior: + scaleDown: + stabilizationWindowSeconds: 300 + policies: + - type: Pods + value: 1 + periodSeconds: 180 + admissionWebhooks: + enabled: false + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml new file mode 100644 index 000000000..b8b3ac686 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml @@ -0,0 +1,11 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + autoscaling: + enabled: true + admissionWebhooks: + enabled: false + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-customconfig-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-customconfig-values.yaml new file mode 100644 index 000000000..174941848 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-customconfig-values.yaml @@ -0,0 +1,12 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + config: + use-proxy-protocol: "true" + allowSnippetAnnotations: false + admissionWebhooks: + enabled: false + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml new file mode 100644 index 000000000..a564eaf93 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml @@ -0,0 +1,20 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: NodePort + nodePorts: + tcp: + 9000: 30090 + udp: + 9001: 30091 + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-default-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-default-values.yaml new file mode 100644 index 000000000..9f46b4e7e --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-default-values.yaml @@ -0,0 +1,8 @@ +# Left blank to test default values +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-extra-modules.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-extra-modules.yaml new file mode 100644 index 000000000..ec5923548 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-extra-modules.yaml @@ -0,0 +1,10 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + service: + type: ClusterIP + extraModules: + - name: opentelemetry + image: busybox diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-headers-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-headers-values.yaml new file mode 100644 index 000000000..17a11ac37 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-headers-values.yaml @@ -0,0 +1,13 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + addHeaders: + X-Frame-Options: deny + proxySetHeaders: + X-Forwarded-Proto: https + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml new file mode 100644 index 000000000..fd8df8de5 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml @@ -0,0 +1,13 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP + internal: + enabled: true + annotations: + service.beta.kubernetes.io/aws-load-balancer-internal: "true" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-metrics-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-metrics-values.yaml new file mode 100644 index 000000000..9209ad5a6 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-metrics-values.yaml @@ -0,0 +1,11 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + metrics: + enabled: true + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-nodeport-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-nodeport-values.yaml new file mode 100644 index 000000000..cd9b32352 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-nodeport-values.yaml @@ -0,0 +1,9 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: NodePort diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-podannotations-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-podannotations-values.yaml new file mode 100644 index 000000000..b48d93c46 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-podannotations-values.yaml @@ -0,0 +1,16 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + metrics: + enabled: true + service: + type: ClusterIP + podAnnotations: + prometheus.io/path: /metrics + prometheus.io/port: "10254" + prometheus.io/scheme: http + prometheus.io/scrape: "true" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-psp-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-psp-values.yaml new file mode 100644 index 000000000..2f332a7b2 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-psp-values.yaml @@ -0,0 +1,10 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + service: + type: ClusterIP + +podSecurityPolicy: + enabled: true diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml new file mode 100644 index 000000000..c51a4e91f --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml @@ -0,0 +1,19 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP + tcp: + configMapNamespace: default + udp: + configMapNamespace: default + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml new file mode 100644 index 000000000..5b45b69dc --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml @@ -0,0 +1,15 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: false + service: + type: ClusterIP + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-values.yaml new file mode 100644 index 000000000..ac0b6e60e --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-tcp-values.yaml @@ -0,0 +1,11 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + service: + type: ClusterIP + +tcp: + 9000: "default/test:8080" + 9001: "default/test:8080" diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml new file mode 100644 index 000000000..6195bb339 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml @@ -0,0 +1,12 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: true + service: + type: ClusterIP + +podSecurityPolicy: + enabled: true diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-resources-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-resources-values.yaml new file mode 100644 index 000000000..49ebbb02c --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-resources-values.yaml @@ -0,0 +1,23 @@ +controller: + service: + type: ClusterIP + admissionWebhooks: + enabled: true + createSecretJob: + resources: + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi + patchWebhookJob: + resources: + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi + patch: + enabled: true diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-values.yaml new file mode 100644 index 000000000..76669a530 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/ci/deployment-webhook-values.yaml @@ -0,0 +1,9 @@ +controller: + image: + repository: ingress-controller/controller + tag: 1.0.0-dev + digest: null + admissionWebhooks: + enabled: true + service: + type: ClusterIP diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/NOTES.txt b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/NOTES.txt new file mode 100644 index 000000000..8985c56c0 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/NOTES.txt @@ -0,0 +1,80 @@ +The ingress-nginx controller has been installed. + +{{- if contains "NodePort" .Values.controller.service.type }} +Get the application URL by running these commands: + +{{- if (not (empty .Values.controller.service.nodePorts.http)) }} + export HTTP_NODE_PORT={{ .Values.controller.service.nodePorts.http }} +{{- else }} + export HTTP_NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get services -o jsonpath="{.spec.ports[0].nodePort}" {{ include "ingress-nginx.controller.fullname" . }}) +{{- end }} +{{- if (not (empty .Values.controller.service.nodePorts.https)) }} + export HTTPS_NODE_PORT={{ .Values.controller.service.nodePorts.https }} +{{- else }} + export HTTPS_NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get services -o jsonpath="{.spec.ports[1].nodePort}" {{ include "ingress-nginx.controller.fullname" . }}) +{{- end }} + export NODE_IP=$(kubectl --namespace {{ .Release.Namespace }} get nodes -o jsonpath="{.items[0].status.addresses[1].address}") + + echo "Visit http://$NODE_IP:$HTTP_NODE_PORT to access your application via HTTP." + echo "Visit https://$NODE_IP:$HTTPS_NODE_PORT to access your application via HTTPS." +{{- else if contains "LoadBalancer" .Values.controller.service.type }} +It may take a few minutes for the LoadBalancer IP to be available. +You can watch the status by running 'kubectl --namespace {{ .Release.Namespace }} get services -o wide -w {{ include "ingress-nginx.controller.fullname" . }}' +{{- else if contains "ClusterIP" .Values.controller.service.type }} +Get the application URL by running these commands: + export POD_NAME=$(kubectl --namespace {{ .Release.Namespace }} get pods -o jsonpath="{.items[0].metadata.name}" -l "app={{ template "ingress-nginx.name" . }},component={{ .Values.controller.name }},release={{ .Release.Name }}") + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 + echo "Visit http://127.0.0.1:8080 to access your application." +{{- end }} + +An example Ingress that makes use of the controller: + +{{- $isV1 := semverCompare ">=1" .Chart.AppVersion}} + apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: example + namespace: foo + {{- if eq $isV1 false }} + annotations: + kubernetes.io/ingress.class: {{ .Values.controller.ingressClass }} + {{- end }} + spec: + {{- if $isV1 }} + ingressClassName: {{ .Values.controller.ingressClassResource.name }} + {{- end }} + rules: + - host: www.example.com + http: + paths: + - pathType: Prefix + backend: + service: + name: exampleService + port: + number: 80 + path: / + # This section is only required if TLS is to be enabled for the Ingress + tls: + - hosts: + - www.example.com + secretName: example-tls + +If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided: + + apiVersion: v1 + kind: Secret + metadata: + name: example-tls + namespace: foo + data: + tls.crt: + tls.key: + type: kubernetes.io/tls + +{{- if .Values.controller.headers }} +################################################################################# +###### WARNING: `controller.headers` has been deprecated! ##### +###### It has been renamed to `controller.proxySetHeaders`. ##### +################################################################################# +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/_helpers.tpl b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/_helpers.tpl new file mode 100644 index 000000000..a72af5d9d --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/_helpers.tpl @@ -0,0 +1,156 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "ingress-nginx.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "ingress-nginx.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + + +{{/* +Container SecurityContext. +*/}} +{{- define "controller.containerSecurityContext" -}} +{{- if .Values.controller.containerSecurityContext -}} +{{- toYaml .Values.controller.containerSecurityContext -}} +{{- else -}} +capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE +runAsUser: {{ .Values.controller.image.runAsUser }} +allowPrivilegeEscalation: {{ .Values.controller.image.allowPrivilegeEscalation }} +{{- end }} +{{- end -}} + +{{/* +Create a default fully qualified controller name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.controller.fullname" -}} +{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.controller.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Construct the path for the publish-service. + +By convention this will simply use the / to match the name of the +service generated. + +Users can provide an override for an explicit service they want bound via `.Values.controller.publishService.pathOverride` + +*/}} +{{- define "ingress-nginx.controller.publishServicePath" -}} +{{- $defServiceName := printf "%s/%s" "$(POD_NAMESPACE)" (include "ingress-nginx.controller.fullname" .) -}} +{{- $servicePath := default $defServiceName .Values.controller.publishService.pathOverride }} +{{- print $servicePath | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified default backend name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.defaultBackend.fullname" -}} +{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.defaultBackend.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "ingress-nginx.labels" -}} +helm.sh/chart: {{ include "ingress-nginx.chart" . }} +{{ include "ingress-nginx.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/part-of: {{ template "ingress-nginx.name" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- if .Values.commonLabels}} +{{ toYaml .Values.commonLabels }} +{{- end }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "ingress-nginx.selectorLabels" -}} +app.kubernetes.io/name: {{ include "ingress-nginx.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the controller service account to use +*/}} +{{- define "ingress-nginx.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "ingress-nginx.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the backend service account to use - only used when podsecuritypolicy is also enabled +*/}} +{{- define "ingress-nginx.defaultBackend.serviceAccountName" -}} +{{- if .Values.defaultBackend.serviceAccount.create -}} + {{ default (printf "%s-backend" (include "ingress-nginx.fullname" .)) .Values.defaultBackend.serviceAccount.name }} +{{- else -}} + {{ default "default-backend" .Values.defaultBackend.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiGroup for PodSecurityPolicy. +*/}} +{{- define "podSecurityPolicy.apiGroup" -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "policy" -}} +{{- else -}} +{{- print "extensions" -}} +{{- end -}} +{{- end -}} + +{{/* +Check the ingress controller version tag is at most three versions behind the last release +*/}} +{{- define "isControllerTagValid" -}} +{{- if not (semverCompare ">=0.27.0-0" .Values.controller.image.tag) -}} +{{- fail "Controller container image tag should be 0.27.0 or higher" -}} +{{- end -}} +{{- end -}} + +{{/* +IngressClass parameters. +*/}} +{{- define "ingressClass.parameters" -}} + {{- if .Values.controller.ingressClassResource.parameters -}} + parameters: +{{ toYaml .Values.controller.ingressClassResource.parameters | indent 4}} + {{ end }} +{{- end -}} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/_params.tpl b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/_params.tpl new file mode 100644 index 000000000..305ce0dd2 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/_params.tpl @@ -0,0 +1,62 @@ +{{- define "ingress-nginx.params" -}} +- /nginx-ingress-controller +{{- if .Values.defaultBackend.enabled }} +- --default-backend-service=$(POD_NAMESPACE)/{{ include "ingress-nginx.defaultBackend.fullname" . }} +{{- end }} +{{- if and .Values.controller.publishService.enabled .Values.controller.service.enabled }} +{{- if .Values.controller.service.external.enabled }} +- --publish-service={{ template "ingress-nginx.controller.publishServicePath" . }} +{{- else if .Values.controller.service.internal.enabled }} +- --publish-service={{ template "ingress-nginx.controller.publishServicePath" . }}-internal +{{- end }} +{{- end }} +- --election-id={{ .Values.controller.electionID }} +- --controller-class={{ .Values.controller.ingressClassResource.controllerValue }} +{{- if .Values.controller.ingressClass }} +- --ingress-class={{ .Values.controller.ingressClass }} +{{- end }} +- --configmap={{ default "$(POD_NAMESPACE)" .Values.controller.configMapNamespace }}/{{ include "ingress-nginx.controller.fullname" . }} +{{- if .Values.tcp }} +- --tcp-services-configmap={{ default "$(POD_NAMESPACE)" .Values.controller.tcp.configMapNamespace }}/{{ include "ingress-nginx.fullname" . }}-tcp +{{- end }} +{{- if .Values.udp }} +- --udp-services-configmap={{ default "$(POD_NAMESPACE)" .Values.controller.udp.configMapNamespace }}/{{ include "ingress-nginx.fullname" . }}-udp +{{- end }} +{{- if .Values.controller.scope.enabled }} +- --watch-namespace={{ default "$(POD_NAMESPACE)" .Values.controller.scope.namespace }} +{{- end }} +{{- if and (not .Values.controller.scope.enabled) .Values.controller.scope.namespaceSelector }} +- --watch-namespace-selector={{ default "" .Values.controller.scope.namespaceSelector }} +{{- end }} +{{- if and .Values.controller.reportNodeInternalIp .Values.controller.hostNetwork }} +- --report-node-internal-ip-address={{ .Values.controller.reportNodeInternalIp }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.enabled }} +- --validating-webhook=:{{ .Values.controller.admissionWebhooks.port }} +- --validating-webhook-certificate={{ .Values.controller.admissionWebhooks.certificate }} +- --validating-webhook-key={{ .Values.controller.admissionWebhooks.key }} +{{- end }} +{{- if .Values.controller.maxmindLicenseKey }} +- --maxmind-license-key={{ .Values.controller.maxmindLicenseKey }} +{{- end }} +{{- if .Values.controller.healthCheckHost }} +- --healthz-host={{ .Values.controller.healthCheckHost }} +{{- end }} +{{- if not (eq .Values.controller.healthCheckPath "/healthz") }} +- --health-check-path={{ .Values.controller.healthCheckPath }} +{{- end }} +{{- if .Values.controller.ingressClassByName }} +- --ingress-class-by-name=true +{{- end }} +{{- if .Values.controller.watchIngressWithoutClass }} +- --watch-ingress-without-class=true +{{- end }} +{{- range $key, $value := .Values.controller.extraArgs }} +{{- /* Accept keys without values or with false as value */}} +{{- if eq ($value | quote | len) 2 }} +- --{{ $key }} +{{- else }} +- --{{ $key }}={{ $value }} +{{- end }} +{{- end }} +{{- end -}} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml new file mode 100644 index 000000000..5659a1f10 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml @@ -0,0 +1,34 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - get + - update +{{- if .Values.podSecurityPolicy.enabled }} + - apiGroups: ['extensions'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + {{- with .Values.controller.admissionWebhooks.existingPsp }} + - {{ . }} + {{- else }} + - {{ include "ingress-nginx.fullname" . }}-admission + {{- end }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml new file mode 100644 index 000000000..abf17fb9f --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "ingress-nginx.fullname" . }}-admission +subjects: + - kind: ServiceAccount + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml new file mode 100644 index 000000000..f20e247f9 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml @@ -0,0 +1,76 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission-create + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + {{- with .Values.controller.admissionWebhooks.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: +{{- if .Capabilities.APIVersions.Has "batch/v1alpha1" }} + # Alpha feature since k8s 1.12 + ttlSecondsAfterFinished: 0 +{{- end }} + template: + metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission-create + {{- if .Values.controller.admissionWebhooks.patch.podAnnotations }} + annotations: {{ toYaml .Values.controller.admissionWebhooks.patch.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 8 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.controller.admissionWebhooks.patch.priorityClassName }} + priorityClassName: {{ .Values.controller.admissionWebhooks.patch.priorityClassName }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + containers: + - name: create + {{- with .Values.controller.admissionWebhooks.patch.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.controller.admissionWebhooks.patch.image.pullPolicy }} + args: + - create + - --host={{ include "ingress-nginx.controller.fullname" . }}-admission,{{ include "ingress-nginx.controller.fullname" . }}-admission.$(POD_NAMESPACE).svc + - --namespace=$(POD_NAMESPACE) + - --secret-name={{ include "ingress-nginx.fullname" . }}-admission + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + securityContext: + allowPrivilegeEscalation: false + {{- if .Values.controller.admissionWebhooks.createSecretJob.resources }} + resources: {{ toYaml .Values.controller.admissionWebhooks.createSecretJob.resources | nindent 12 }} + {{- end }} + restartPolicy: OnFailure + serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission + {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} + nodeSelector: {{ toYaml .Values.controller.admissionWebhooks.patch.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.patch.tolerations }} + tolerations: {{ toYaml .Values.controller.admissionWebhooks.patch.tolerations | nindent 8 }} + {{- end }} + securityContext: + runAsNonRoot: true + runAsUser: {{ .Values.controller.admissionWebhooks.patch.runAsUser }} + fsGroup: {{ .Values.controller.admissionWebhooks.patch.fsGroup }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml new file mode 100644 index 000000000..8583685fa --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml @@ -0,0 +1,78 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission-patch + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + {{- with .Values.controller.admissionWebhooks.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: +{{- if .Capabilities.APIVersions.Has "batch/v1alpha1" }} + # Alpha feature since k8s 1.12 + ttlSecondsAfterFinished: 0 +{{- end }} + template: + metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission-patch + {{- if .Values.controller.admissionWebhooks.patch.podAnnotations }} + annotations: {{ toYaml .Values.controller.admissionWebhooks.patch.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 8 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.controller.admissionWebhooks.patch.priorityClassName }} + priorityClassName: {{ .Values.controller.admissionWebhooks.patch.priorityClassName }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + containers: + - name: patch + {{- with .Values.controller.admissionWebhooks.patch.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.controller.admissionWebhooks.patch.image.pullPolicy }} + args: + - patch + - --webhook-name={{ include "ingress-nginx.fullname" . }}-admission + - --namespace=$(POD_NAMESPACE) + - --patch-mutating=false + - --secret-name={{ include "ingress-nginx.fullname" . }}-admission + - --patch-failure-policy={{ .Values.controller.admissionWebhooks.failurePolicy }} + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + securityContext: + allowPrivilegeEscalation: false + {{- if .Values.controller.admissionWebhooks.patchWebhookJob.resources }} + resources: {{ toYaml .Values.controller.admissionWebhooks.patchWebhookJob.resources | nindent 12 }} + {{- end }} + restartPolicy: OnFailure + serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission + {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} + nodeSelector: {{ toYaml .Values.controller.admissionWebhooks.patch.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.patch.tolerations }} + tolerations: {{ toYaml .Values.controller.admissionWebhooks.patch.tolerations | nindent 8 }} + {{- end }} + securityContext: + runAsNonRoot: true + runAsUser: {{ .Values.controller.admissionWebhooks.patch.runAsUser }} + fsGroup: {{ .Values.controller.admissionWebhooks.patch.fsGroup }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml new file mode 100644 index 000000000..70edde334 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled .Values.podSecurityPolicy.enabled (empty .Values.controller.admissionWebhooks.existingPsp) -}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + allowPrivilegeEscalation: false + fsGroup: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + requiredDropCapabilities: + - ALL + runAsUser: + rule: MustRunAsNonRoot + seLinux: + rule: RunAsAny + supplementalGroups: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml new file mode 100644 index 000000000..795bac6b9 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml new file mode 100644 index 000000000..698c5c864 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "ingress-nginx.fullname" . }}-admission +subjects: + - kind: ServiceAccount + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml new file mode 100644 index 000000000..eae475118 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.patch.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml new file mode 100644 index 000000000..8caffcb03 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml @@ -0,0 +1,48 @@ +{{- if .Values.controller.admissionWebhooks.enabled -}} +# before changing this value, check the required kubernetes version +# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#prerequisites +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + {{- if .Values.controller.admissionWebhooks.annotations }} + annotations: {{ toYaml .Values.controller.admissionWebhooks.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + {{- with .Values.controller.admissionWebhooks.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }}-admission +webhooks: + - name: validate.nginx.ingress.kubernetes.io + matchPolicy: Equivalent + rules: + - apiGroups: + - networking.k8s.io + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - ingresses + failurePolicy: {{ .Values.controller.admissionWebhooks.failurePolicy | default "Fail" }} + sideEffects: None + admissionReviewVersions: + - v1 + clientConfig: + service: + namespace: {{ .Release.Namespace | quote }} + name: {{ include "ingress-nginx.controller.fullname" . }}-admission + path: /networking/v1/ingresses + {{- if .Values.controller.admissionWebhooks.timeoutSeconds }} + timeoutSeconds: {{ .Values.controller.admissionWebhooks.timeoutSeconds }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.namespaceSelector }} + namespaceSelector: {{ toYaml .Values.controller.admissionWebhooks.namespaceSelector | nindent 6 }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.objectSelector }} + objectSelector: {{ toYaml .Values.controller.admissionWebhooks.objectSelector | nindent 6 }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/clusterrole.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/clusterrole.yaml new file mode 100644 index 000000000..c093f048a --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/clusterrole.yaml @@ -0,0 +1,87 @@ +{{- if .Values.rbac.create }} + +{{- if and .Values.rbac.scope (not .Values.controller.scope.enabled) -}} + {{ required "Invalid configuration: 'rbac.scope' should be equal to 'controller.scope.enabled' (true/false)." (index (dict) ".") }} +{{- end }} + +{{- if not .Values.rbac.scope -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }} +rules: + - apiGroups: + - "" + resources: + - configmaps + - endpoints + - nodes + - pods + - secrets +{{- if not .Values.controller.scope.enabled }} + - namespaces +{{- end}} + verbs: + - list + - watch +{{- if and .Values.controller.scope.enabled .Values.controller.scope.namespace }} + - apiGroups: + - "" + resources: + - namespaces + resourceNames: + - "{{ .Values.controller.scope.namespace }}" + verbs: + - get +{{- end }} + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch +{{- end }} + +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/clusterrolebinding.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..acbbd8b10 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/clusterrolebinding.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.rbac.create (not .Values.rbac.scope) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "ingress-nginx.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "ingress-nginx.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml new file mode 100644 index 000000000..dfd49a126 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml @@ -0,0 +1,14 @@ +{{- if .Values.controller.addHeaders -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }}-custom-add-headers + namespace: {{ .Release.Namespace }} +data: {{ toYaml .Values.controller.addHeaders | nindent 2 }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml new file mode 100644 index 000000000..f8d15faf9 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml @@ -0,0 +1,19 @@ +{{- if or .Values.controller.proxySetHeaders .Values.controller.headers -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }}-custom-proxy-headers + namespace: {{ .Release.Namespace }} +data: +{{- if .Values.controller.proxySetHeaders }} +{{ toYaml .Values.controller.proxySetHeaders | indent 2 }} +{{ else if and .Values.controller.headers (not .Values.controller.proxySetHeaders) }} +{{ toYaml .Values.controller.headers | indent 2 }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-tcp.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-tcp.yaml new file mode 100644 index 000000000..0f6088ea9 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-tcp.yaml @@ -0,0 +1,17 @@ +{{- if .Values.tcp -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- if .Values.controller.tcp.annotations }} + annotations: {{ toYaml .Values.controller.tcp.annotations | nindent 4 }} +{{- end }} + name: {{ include "ingress-nginx.fullname" . }}-tcp + namespace: {{ .Release.Namespace }} +data: {{ tpl (toYaml .Values.tcp) . | nindent 2 }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-udp.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-udp.yaml new file mode 100644 index 000000000..3772ec514 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap-udp.yaml @@ -0,0 +1,17 @@ +{{- if .Values.udp -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- if .Values.controller.udp.annotations }} + annotations: {{ toYaml .Values.controller.udp.annotations | nindent 4 }} +{{- end }} + name: {{ include "ingress-nginx.fullname" . }}-udp + namespace: {{ .Release.Namespace }} +data: {{ tpl (toYaml .Values.udp) . | nindent 2 }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap.yaml new file mode 100644 index 000000000..f28b26e1e --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-configmap.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- if .Values.controller.configAnnotations }} + annotations: {{ toYaml .Values.controller.configAnnotations | nindent 4 }} +{{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} +data: + allow-snippet-annotations: "{{ .Values.controller.allowSnippetAnnotations }}" +{{- if .Values.controller.addHeaders }} + add-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers +{{- end }} +{{- if or .Values.controller.proxySetHeaders .Values.controller.headers }} + proxy-set-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-proxy-headers +{{- end }} +{{- if .Values.dhParam }} + ssl-dh-param: {{ printf "%s/%s" .Release.Namespace (include "ingress-nginx.controller.fullname" .) }} +{{- end }} +{{- range $key, $value := .Values.controller.config }} + {{- $key | nindent 2 }}: {{ $value | quote }} +{{- end }} + diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-daemonset.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-daemonset.yaml new file mode 100644 index 000000000..72811fbe4 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-daemonset.yaml @@ -0,0 +1,227 @@ +{{- if or (eq .Values.controller.kind "DaemonSet") (eq .Values.controller.kind "Both") -}} +{{- include "isControllerTagValid" . -}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} + {{- if .Values.controller.annotations }} + annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- if .Values.controller.updateStrategy }} + updateStrategy: {{ toYaml .Values.controller.updateStrategy | nindent 4 }} + {{- end }} + minReadySeconds: {{ .Values.controller.minReadySeconds }} + template: + metadata: + {{- if .Values.controller.podAnnotations }} + annotations: + {{- range $key, $value := .Values.controller.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + labels: + {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.controller.podLabels }} + {{- toYaml .Values.controller.podLabels | nindent 8 }} + {{- end }} + spec: + {{- if .Values.controller.dnsConfig }} + dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} + {{- end }} + {{- if .Values.controller.hostname }} + hostname: {{ toYaml .Values.controller.hostname | nindent 8 }} + {{- end }} + dnsPolicy: {{ .Values.controller.dnsPolicy }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + {{- if .Values.controller.priorityClassName }} + priorityClassName: {{ .Values.controller.priorityClassName }} + {{- end }} + {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }} + securityContext: + {{- end }} + {{- if .Values.controller.podSecurityContext }} + {{- toYaml .Values.controller.podSecurityContext | nindent 8 }} + {{- end }} + {{- if .Values.controller.sysctls }} + sysctls: + {{- range $sysctl, $value := .Values.controller.sysctls }} + - name: {{ $sysctl | quote }} + value: {{ $value | quote }} + {{- end }} + {{- end }} + containers: + - name: {{ .Values.controller.containerName }} + {{- with .Values.controller.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.controller.image.pullPolicy }} + {{- if .Values.controller.lifecycle }} + lifecycle: {{ toYaml .Values.controller.lifecycle | nindent 12 }} + {{- end }} + args: + {{- include "ingress-nginx.params" . | nindent 12 }} + securityContext: + capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE + runAsUser: {{ .Values.controller.image.runAsUser }} + allowPrivilegeEscalation: {{ .Values.controller.image.allowPrivilegeEscalation }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if .Values.controller.enableMimalloc }} + - name: LD_PRELOAD + value: /usr/local/lib/libmimalloc.so + {{- end }} + {{- if .Values.controller.extraEnvs }} + {{- toYaml .Values.controller.extraEnvs | nindent 12 }} + {{- end }} + {{- if .Values.controller.startupProbe }} + startupProbe: {{ toYaml .Values.controller.startupProbe | nindent 12 }} + {{- end }} + livenessProbe: {{ toYaml .Values.controller.livenessProbe | nindent 12 }} + readinessProbe: {{ toYaml .Values.controller.readinessProbe | nindent 12 }} + ports: + {{- range $key, $value := .Values.controller.containerPort }} + - name: {{ $key }} + containerPort: {{ $value }} + protocol: TCP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ index $.Values.controller.hostPort.ports $key | default $value }} + {{- end }} + {{- end }} + {{- if .Values.controller.metrics.enabled }} + - name: metrics + containerPort: {{ .Values.controller.metrics.port }} + protocol: TCP + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook + containerPort: {{ .Values.controller.admissionWebhooks.port }} + protocol: TCP + {{- end }} + {{- range $key, $value := .Values.tcp }} + - name: {{ $key }}-tcp + containerPort: {{ $key }} + protocol: TCP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ $key }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.udp }} + - name: {{ $key }}-udp + containerPort: {{ $key }} + protocol: UDP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ $key }} + {{- end }} + {{- end }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraModules) }} + volumeMounts: + {{- if .Values.controller.extraModules }} + - name: modules + mountPath: /modules_mount + {{- end }} + {{- if .Values.controller.customTemplate.configMapName }} + - mountPath: /etc/nginx/template + name: nginx-template-volume + readOnly: true + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook-cert + mountPath: /usr/local/certificates/ + readOnly: true + {{- end }} + {{- if .Values.controller.extraVolumeMounts }} + {{- toYaml .Values.controller.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.controller.resources }} + resources: {{ toYaml .Values.controller.resources | nindent 12 }} + {{- end }} + {{- if .Values.controller.extraContainers }} + {{ toYaml .Values.controller.extraContainers | nindent 8 }} + {{- end }} + + + {{- if (or .Values.controller.extraInitContainers .Values.controller.extraModules) }} + initContainers: + {{- if .Values.controller.extraInitContainers }} + {{ toYaml .Values.controller.extraInitContainers | nindent 8 }} + {{- end }} + {{- if .Values.controller.extraModules }} + {{- range .Values.controller.extraModules }} + - name: {{ .Name }} + image: {{ .Image }} + command: ['sh', '-c', '/usr/local/bin/init_module.sh'] + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.controller.hostNetwork }} + hostNetwork: {{ .Values.controller.hostNetwork }} + {{- end }} + {{- if .Values.controller.nodeSelector }} + nodeSelector: {{ toYaml .Values.controller.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: {{ toYaml .Values.controller.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.controller.affinity }} + affinity: {{ toYaml .Values.controller.affinity | nindent 8 }} + {{- end }} + {{- if .Values.controller.topologySpreadConstraints }} + topologySpreadConstraints: {{ toYaml .Values.controller.topologySpreadConstraints | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "ingress-nginx.serviceAccountName" . }} + terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraVolumes .Values.controller.extraModules) }} + volumes: + {{- if .Values.controller.extraModules }} + - name: modules + emptyDir: {} + {{- end }} + {{- if .Values.controller.customTemplate.configMapName }} + - name: nginx-template-volume + configMap: + name: {{ .Values.controller.customTemplate.configMapName }} + items: + - key: {{ .Values.controller.customTemplate.configMapKey }} + path: nginx.tmpl + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook-cert + secret: + secretName: {{ include "ingress-nginx.fullname" . }}-admission + {{- end }} + {{- if .Values.controller.extraVolumes }} + {{ toYaml .Values.controller.extraVolumes | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-deployment.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-deployment.yaml new file mode 100644 index 000000000..a1943cd91 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-deployment.yaml @@ -0,0 +1,225 @@ +{{- if or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both") -}} +{{- include "isControllerTagValid" . -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} + {{- if .Values.controller.annotations }} + annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller + {{- if not .Values.controller.autoscaling.enabled }} + replicas: {{ .Values.controller.replicaCount }} + {{- end }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- if .Values.controller.updateStrategy }} + strategy: + {{ toYaml .Values.controller.updateStrategy | nindent 4 }} + {{- end }} + minReadySeconds: {{ .Values.controller.minReadySeconds }} + template: + metadata: + {{- if .Values.controller.podAnnotations }} + annotations: + {{- range $key, $value := .Values.controller.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + labels: + {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.controller.podLabels }} + {{- toYaml .Values.controller.podLabels | nindent 8 }} + {{- end }} + spec: + {{- if .Values.controller.dnsConfig }} + dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} + {{- end }} + {{- if .Values.controller.hostname }} + hostname: {{ toYaml .Values.controller.hostname | nindent 8 }} + {{- end }} + dnsPolicy: {{ .Values.controller.dnsPolicy }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + {{- if .Values.controller.priorityClassName }} + priorityClassName: {{ .Values.controller.priorityClassName | quote }} + {{- end }} + {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }} + securityContext: + {{- end }} + {{- if .Values.controller.podSecurityContext }} + {{- toYaml .Values.controller.podSecurityContext | nindent 8 }} + {{- end }} + {{- if .Values.controller.sysctls }} + sysctls: + {{- range $sysctl, $value := .Values.controller.sysctls }} + - name: {{ $sysctl | quote }} + value: {{ $value | quote }} + {{- end }} + {{- end }} + containers: + - name: {{ .Values.controller.containerName }} + {{- with .Values.controller.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.controller.image.pullPolicy }} + {{- if .Values.controller.lifecycle }} + lifecycle: {{ toYaml .Values.controller.lifecycle | nindent 12 }} + {{- end }} + args: + {{- include "ingress-nginx.params" . | nindent 12 }} + securityContext: {{ include "controller.containerSecurityContext" . | nindent 12 }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if .Values.controller.enableMimalloc }} + - name: LD_PRELOAD + value: /usr/local/lib/libmimalloc.so + {{- end }} + {{- if .Values.controller.extraEnvs }} + {{- toYaml .Values.controller.extraEnvs | nindent 12 }} + {{- end }} + {{- if .Values.controller.startupProbe }} + startupProbe: {{ toYaml .Values.controller.startupProbe | nindent 12 }} + {{- end }} + livenessProbe: {{ toYaml .Values.controller.livenessProbe | nindent 12 }} + readinessProbe: {{ toYaml .Values.controller.readinessProbe | nindent 12 }} + ports: + {{- range $key, $value := .Values.controller.containerPort }} + - name: {{ $key }} + containerPort: {{ $value }} + protocol: TCP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ index $.Values.controller.hostPort.ports $key | default $value }} + {{- end }} + {{- end }} + {{- if .Values.controller.metrics.enabled }} + - name: metrics + containerPort: {{ .Values.controller.metrics.port }} + protocol: TCP + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook + containerPort: {{ .Values.controller.admissionWebhooks.port }} + protocol: TCP + {{- end }} + {{- range $key, $value := .Values.tcp }} + - name: {{ $key }}-tcp + containerPort: {{ $key }} + protocol: TCP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ $key }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.udp }} + - name: {{ $key }}-udp + containerPort: {{ $key }} + protocol: UDP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ $key }} + {{- end }} + {{- end }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraModules) }} + volumeMounts: + {{- if .Values.controller.extraModules }} + - name: modules + mountPath: /modules_mount + {{- end }} + {{- if .Values.controller.customTemplate.configMapName }} + - mountPath: /etc/nginx/template + name: nginx-template-volume + readOnly: true + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook-cert + mountPath: /usr/local/certificates/ + readOnly: true + {{- end }} + {{- if .Values.controller.extraVolumeMounts }} + {{- toYaml .Values.controller.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.controller.resources }} + resources: {{ toYaml .Values.controller.resources | nindent 12 }} + {{- end }} + {{- if .Values.controller.extraContainers }} + {{ toYaml .Values.controller.extraContainers | nindent 8 }} + {{- end }} + {{- if (or .Values.controller.extraInitContainers .Values.controller.extraModules) }} + initContainers: + {{- if .Values.controller.extraInitContainers }} + {{ toYaml .Values.controller.extraInitContainers | nindent 8 }} + {{- end }} + {{- if .Values.controller.extraModules }} + {{- range .Values.controller.extraModules }} + - name: {{ .name }} + image: {{ .image }} + command: ['sh', '-c', '/usr/local/bin/init_module.sh'] + volumeMounts: + - name: modules + mountPath: /modules_mount + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.controller.hostNetwork }} + hostNetwork: {{ .Values.controller.hostNetwork }} + {{- end }} + {{- if .Values.controller.nodeSelector }} + nodeSelector: {{ toYaml .Values.controller.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: {{ toYaml .Values.controller.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.controller.affinity }} + affinity: {{ toYaml .Values.controller.affinity | nindent 8 }} + {{- end }} + {{- if .Values.controller.topologySpreadConstraints }} + topologySpreadConstraints: {{ toYaml .Values.controller.topologySpreadConstraints | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "ingress-nginx.serviceAccountName" . }} + terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraVolumes .Values.controller.extraModules) }} + volumes: + {{- if .Values.controller.extraModules }} + - name: modules + emptyDir: {} + {{- end }} + {{- if .Values.controller.customTemplate.configMapName }} + - name: nginx-template-volume + configMap: + name: {{ .Values.controller.customTemplate.configMapName }} + items: + - key: {{ .Values.controller.customTemplate.configMapKey }} + path: nginx.tmpl + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook-cert + secret: + secretName: {{ include "ingress-nginx.fullname" . }}-admission + {{- end }} + {{- if .Values.controller.extraVolumes }} + {{ toYaml .Values.controller.extraVolumes | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-hpa.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-hpa.yaml new file mode 100644 index 000000000..e0979f14b --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-hpa.yaml @@ -0,0 +1,52 @@ +{{- if and .Values.controller.autoscaling.enabled (or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both")) -}} +{{- if not .Values.controller.keda.enabled }} + +apiVersion: autoscaling/v2beta2 +kind: HorizontalPodAutoscaler +metadata: + annotations: + {{- with .Values.controller.autoscaling.annotations }} + {{- toYaml . | trimSuffix "\n" | nindent 4 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "ingress-nginx.controller.fullname" . }} + minReplicas: {{ .Values.controller.autoscaling.minReplicas }} + maxReplicas: {{ .Values.controller.autoscaling.maxReplicas }} + metrics: + {{- with .Values.controller.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} + {{- with .Values.controller.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} + {{- with .Values.controller.autoscalingTemplate }} + {{- toYaml . | nindent 2 }} + {{- end }} + {{- with .Values.controller.autoscaling.behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} + diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-ingressclass.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-ingressclass.yaml new file mode 100644 index 000000000..9492784a2 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-ingressclass.yaml @@ -0,0 +1,21 @@ +{{- if .Values.controller.ingressClassResource.enabled -}} +# We don't support namespaced ingressClass yet +# So a ClusterRole and a ClusterRoleBinding is required +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ .Values.controller.ingressClassResource.name }} +{{- if .Values.controller.ingressClassResource.default }} + annotations: + ingressclass.kubernetes.io/is-default-class: "true" +{{- end }} +spec: + controller: {{ .Values.controller.ingressClassResource.controllerValue }} + {{ template "ingressClass.parameters" . }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-keda.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-keda.yaml new file mode 100644 index 000000000..875157ea4 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-keda.yaml @@ -0,0 +1,42 @@ +{{- if and .Values.controller.keda.enabled (or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both")) -}} +# https://keda.sh/docs/ + +apiVersion: {{ .Values.controller.keda.apiVersion }} +kind: ScaledObject +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + {{- if .Values.controller.keda.scaledObject.annotations }} + annotations: {{ toYaml .Values.controller.keda.scaledObject.annotations | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: +{{- if eq .Values.controller.keda.apiVersion "keda.k8s.io/v1alpha1" }} + deploymentName: {{ include "ingress-nginx.controller.fullname" . }} +{{- else if eq .Values.controller.keda.apiVersion "keda.sh/v1alpha1" }} + name: {{ include "ingress-nginx.controller.fullname" . }} +{{- end }} + pollingInterval: {{ .Values.controller.keda.pollingInterval }} + cooldownPeriod: {{ .Values.controller.keda.cooldownPeriod }} + minReplicaCount: {{ .Values.controller.keda.minReplicas }} + maxReplicaCount: {{ .Values.controller.keda.maxReplicas }} + triggers: +{{- with .Values.controller.keda.triggers }} +{{ toYaml . | indent 2 }} +{{ end }} + advanced: + restoreToOriginalReplicaCount: {{ .Values.controller.keda.restoreToOriginalReplicaCount }} +{{- if .Values.controller.keda.behavior }} + horizontalPodAutoscalerConfig: + behavior: +{{ with .Values.controller.keda.behavior -}} +{{ toYaml . | indent 8 }} +{{ end }} + +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml new file mode 100644 index 000000000..8dfbe9891 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml @@ -0,0 +1,19 @@ +{{- if or (and .Values.controller.autoscaling.enabled (gt (.Values.controller.autoscaling.minReplicas | int) 1)) (and (not .Values.controller.autoscaling.enabled) (gt (.Values.controller.replicaCount | int) 1)) }} +apiVersion: {{ ternary "policy/v1" "policy/v1beta1" (semverCompare ">=1.21.0-0" .Capabilities.KubeVersion.Version) }} +kind: PodDisruptionBudget +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller + minAvailable: {{ .Values.controller.minAvailable }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-prometheusrules.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-prometheusrules.yaml new file mode 100644 index 000000000..ca5427523 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-prometheusrules.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.controller.metrics.enabled .Values.controller.metrics.prometheusRule.enabled -}} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "ingress-nginx.controller.fullname" . }} +{{- if .Values.controller.metrics.prometheusRule.namespace }} + namespace: {{ .Values.controller.metrics.prometheusRule.namespace | quote }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.metrics.prometheusRule.additionalLabels }} + {{- toYaml .Values.controller.metrics.prometheusRule.additionalLabels | nindent 4 }} + {{- end }} +spec: +{{- if .Values.controller.metrics.prometheusRule.rules }} + groups: + - name: {{ template "ingress-nginx.name" . }} + rules: {{- toYaml .Values.controller.metrics.prometheusRule.rules | nindent 4 }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-psp.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-psp.yaml new file mode 100644 index 000000000..a859594d1 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-psp.yaml @@ -0,0 +1,89 @@ +{{- if and .Values.podSecurityPolicy.enabled (empty .Values.controller.existingPsp) -}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "ingress-nginx.fullname" . }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + allowedCapabilities: + - NET_BIND_SERVICE +{{- if .Values.controller.sysctls }} + allowedUnsafeSysctls: + {{- range $sysctl, $value := .Values.controller.sysctls }} + - {{ $sysctl }} + {{- end }} +{{- end }} + privileged: false + allowPrivilegeEscalation: true + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + #- 'projected' + - 'secret' + #- 'downwardAPI' +{{- if .Values.controller.hostNetwork }} + hostNetwork: {{ .Values.controller.hostNetwork }} +{{- end }} +{{- if or .Values.controller.hostNetwork .Values.controller.hostPort.enabled }} + hostPorts: +{{- if .Values.controller.hostNetwork }} +{{- range $key, $value := .Values.controller.containerPort }} + # {{ $key }} + - min: {{ $value }} + max: {{ $value }} +{{- end }} +{{- else if .Values.controller.hostPort.enabled }} +{{- range $key, $value := .Values.controller.hostPort.ports }} + # {{ $key }} + - min: {{ $value }} + max: {{ $value }} +{{- end }} +{{- end }} +{{- if .Values.controller.metrics.enabled }} + # metrics + - min: {{ .Values.controller.metrics.port }} + max: {{ .Values.controller.metrics.port }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.enabled }} + # admission webhooks + - min: {{ .Values.controller.admissionWebhooks.port }} + max: {{ .Values.controller.admissionWebhooks.port }} +{{- end }} +{{- range $key, $value := .Values.tcp }} + # {{ $key }}-tcp + - min: {{ $key }} + max: {{ $key }} +{{- end }} +{{- range $key, $value := .Values.udp }} + # {{ $key }}-udp + - min: {{ $key }} + max: {{ $key }} +{{- end }} +{{- end }} + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: 'MustRunAsNonRoot' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false + seLinux: + rule: 'RunAsAny' +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-role.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-role.yaml new file mode 100644 index 000000000..47bbc32d0 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-role.yaml @@ -0,0 +1,93 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }} + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - apiGroups: + - "" + resources: + - configmaps + - pods + - secrets + - endpoints + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - networking.k8s.io + resources: + - ingressclasses + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - configmaps + resourceNames: + - {{ .Values.controller.electionID }} + verbs: + - get + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +{{- if .Values.podSecurityPolicy.enabled }} + - apiGroups: [{{ template "podSecurityPolicy.apiGroup" . }}] + resources: ['podsecuritypolicies'] + verbs: ['use'] + {{- with .Values.controller.existingPsp }} + resourceNames: [{{ . }}] + {{- else }} + resourceNames: [{{ include "ingress-nginx.fullname" . }}] + {{- end }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-rolebinding.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-rolebinding.yaml new file mode 100644 index 000000000..e846a1183 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "ingress-nginx.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "ingress-nginx.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-internal.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-internal.yaml new file mode 100644 index 000000000..599449836 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-internal.yaml @@ -0,0 +1,79 @@ +{{- if and .Values.controller.service.enabled .Values.controller.service.internal.enabled .Values.controller.service.internal.annotations}} +apiVersion: v1 +kind: Service +metadata: + annotations: + {{- range $key, $value := .Values.controller.service.internal.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.service.labels }} + {{- toYaml .Values.controller.service.labels | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }}-internal + namespace: {{ .Release.Namespace }} +spec: + type: "{{ .Values.controller.service.type }}" +{{- if .Values.controller.service.internal.loadBalancerIP }} + loadBalancerIP: {{ .Values.controller.service.internal.loadBalancerIP }} +{{- end }} +{{- if .Values.controller.service.internal.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.controller.service.internal.loadBalancerSourceRanges | nindent 4 }} +{{- end }} +{{- if .Values.controller.service.internal.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.controller.service.internal.externalTrafficPolicy }} +{{- end }} + ports: + {{- $setNodePorts := (or (eq .Values.controller.service.type "NodePort") (eq .Values.controller.service.type "LoadBalancer")) }} + {{- if .Values.controller.service.enableHttp }} + - name: http + port: {{ .Values.controller.service.ports.http }} + protocol: TCP + targetPort: {{ .Values.controller.service.targetPorts.http }} + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: http + {{- end }} + {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.http))) }} + nodePort: {{ .Values.controller.service.nodePorts.http }} + {{- end }} + {{- end }} + {{- if .Values.controller.service.enableHttps }} + - name: https + port: {{ .Values.controller.service.ports.https }} + protocol: TCP + targetPort: {{ .Values.controller.service.targetPorts.https }} + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: https + {{- end }} + {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.https))) }} + nodePort: {{ .Values.controller.service.nodePorts.https }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.tcp }} + - name: {{ $key }}-tcp + port: {{ $key }} + protocol: TCP + targetPort: {{ $key }}-tcp + {{- if $.Values.controller.service.nodePorts.tcp }} + {{- if index $.Values.controller.service.nodePorts.tcp $key }} + nodePort: {{ index $.Values.controller.service.nodePorts.tcp $key }} + {{- end }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.udp }} + - name: {{ $key }}-udp + port: {{ $key }} + protocol: UDP + targetPort: {{ $key }}-udp + {{- if $.Values.controller.service.nodePorts.udp }} + {{- if index $.Values.controller.service.nodePorts.udp $key }} + nodePort: {{ index $.Values.controller.service.nodePorts.udp $key }} + {{- end }} + {{- end }} + {{- end }} + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-metrics.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-metrics.yaml new file mode 100644 index 000000000..0aaf41473 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-metrics.yaml @@ -0,0 +1,45 @@ +{{- if .Values.controller.metrics.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.controller.metrics.service.annotations }} + annotations: {{ toYaml .Values.controller.metrics.service.annotations | nindent 4 }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.metrics.service.labels }} + {{- toYaml .Values.controller.metrics.service.labels | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }}-metrics + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.controller.metrics.service.type }} +{{- if .Values.controller.metrics.service.clusterIP }} + clusterIP: {{ .Values.controller.metrics.service.clusterIP }} +{{- end }} +{{- if .Values.controller.metrics.service.externalIPs }} + externalIPs: {{ toYaml .Values.controller.metrics.service.externalIPs | nindent 4 }} +{{- end }} +{{- if .Values.controller.metrics.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.controller.metrics.service.loadBalancerIP }} +{{- end }} +{{- if .Values.controller.metrics.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.controller.metrics.service.loadBalancerSourceRanges | nindent 4 }} +{{- end }} +{{- if .Values.controller.metrics.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.controller.metrics.service.externalTrafficPolicy }} +{{- end }} + ports: + - name: metrics + port: {{ .Values.controller.metrics.service.servicePort }} + protocol: TCP + targetPort: metrics + {{- $setNodePorts := (or (eq .Values.controller.metrics.service.type "NodePort") (eq .Values.controller.metrics.service.type "LoadBalancer")) }} + {{- if (and $setNodePorts (not (empty .Values.controller.metrics.service.nodePort))) }} + nodePort: {{ .Values.controller.metrics.service.nodePort }} + {{- end }} + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-webhook.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-webhook.yaml new file mode 100644 index 000000000..2aae24fcf --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service-webhook.yaml @@ -0,0 +1,40 @@ +{{- if .Values.controller.admissionWebhooks.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.controller.admissionWebhooks.service.annotations }} + annotations: {{ toYaml .Values.controller.admissionWebhooks.service.annotations | nindent 4 }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }}-admission + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.controller.admissionWebhooks.service.type }} +{{- if .Values.controller.admissionWebhooks.service.clusterIP }} + clusterIP: {{ .Values.controller.admissionWebhooks.service.clusterIP }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.service.externalIPs }} + externalIPs: {{ toYaml .Values.controller.admissionWebhooks.service.externalIPs | nindent 4 }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.controller.admissionWebhooks.service.loadBalancerIP }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.controller.admissionWebhooks.service.loadBalancerSourceRanges | nindent 4 }} +{{- end }} + ports: + - name: https-webhook + port: 443 + targetPort: webhook + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: https + {{- end }} + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service.yaml new file mode 100644 index 000000000..05fb2041e --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-service.yaml @@ -0,0 +1,101 @@ +{{- if and .Values.controller.service.enabled .Values.controller.service.external.enabled -}} +apiVersion: v1 +kind: Service +metadata: + annotations: + {{- range $key, $value := .Values.controller.service.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.service.labels }} + {{- toYaml .Values.controller.service.labels | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.controller.service.type }} +{{- if .Values.controller.service.clusterIP }} + clusterIP: {{ .Values.controller.service.clusterIP }} +{{- end }} +{{- if .Values.controller.service.externalIPs }} + externalIPs: {{ toYaml .Values.controller.service.externalIPs | nindent 4 }} +{{- end }} +{{- if .Values.controller.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.controller.service.loadBalancerIP }} +{{- end }} +{{- if .Values.controller.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.controller.service.loadBalancerSourceRanges | nindent 4 }} +{{- end }} +{{- if .Values.controller.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.controller.service.externalTrafficPolicy }} +{{- end }} +{{- if .Values.controller.service.sessionAffinity }} + sessionAffinity: {{ .Values.controller.service.sessionAffinity }} +{{- end }} +{{- if .Values.controller.service.healthCheckNodePort }} + healthCheckNodePort: {{ .Values.controller.service.healthCheckNodePort }} +{{- end }} +{{- if semverCompare ">=1.20.0-0" .Capabilities.KubeVersion.Version -}} +{{- if .Values.controller.service.ipFamilyPolicy }} + ipFamilyPolicy: {{ .Values.controller.service.ipFamilyPolicy }} +{{- end }} +{{- end }} +{{- if semverCompare ">=1.20.0-0" .Capabilities.KubeVersion.Version -}} +{{- if .Values.controller.service.ipFamilies }} + ipFamilies: {{ toYaml .Values.controller.service.ipFamilies | nindent 4 }} +{{- end }} +{{- end }} + ports: + {{- $setNodePorts := (or (eq .Values.controller.service.type "NodePort") (eq .Values.controller.service.type "LoadBalancer")) }} + {{- if .Values.controller.service.enableHttp }} + - name: http + port: {{ .Values.controller.service.ports.http }} + protocol: TCP + targetPort: {{ .Values.controller.service.targetPorts.http }} + {{- if and (semverCompare ">=1.20" .Capabilities.KubeVersion.Version) (.Values.controller.service.appProtocol) }} + appProtocol: http + {{- end }} + {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.http))) }} + nodePort: {{ .Values.controller.service.nodePorts.http }} + {{- end }} + {{- end }} + {{- if .Values.controller.service.enableHttps }} + - name: https + port: {{ .Values.controller.service.ports.https }} + protocol: TCP + targetPort: {{ .Values.controller.service.targetPorts.https }} + {{- if and (semverCompare ">=1.20" .Capabilities.KubeVersion.Version) (.Values.controller.service.appProtocol) }} + appProtocol: https + {{- end }} + {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.https))) }} + nodePort: {{ .Values.controller.service.nodePorts.https }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.tcp }} + - name: {{ $key }}-tcp + port: {{ $key }} + protocol: TCP + targetPort: {{ $key }}-tcp + {{- if $.Values.controller.service.nodePorts.tcp }} + {{- if index $.Values.controller.service.nodePorts.tcp $key }} + nodePort: {{ index $.Values.controller.service.nodePorts.tcp $key }} + {{- end }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.udp }} + - name: {{ $key }}-udp + port: {{ $key }} + protocol: UDP + targetPort: {{ $key }}-udp + {{- if $.Values.controller.service.nodePorts.udp }} + {{- if index $.Values.controller.service.nodePorts.udp $key }} + nodePort: {{ index $.Values.controller.service.nodePorts.udp $key }} + {{- end }} + {{- end }} + {{- end }} + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-serviceaccount.yaml new file mode 100644 index 000000000..824b2a124 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- if or .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ template "ingress-nginx.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} + {{- if .Values.serviceAccount.annotations }} + annotations: + {{ toYaml .Values.serviceAccount.annotations | indent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-servicemonitor.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-servicemonitor.yaml new file mode 100644 index 000000000..4dbc6da9f --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/controller-servicemonitor.yaml @@ -0,0 +1,48 @@ +{{- if and .Values.controller.metrics.enabled .Values.controller.metrics.serviceMonitor.enabled -}} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "ingress-nginx.controller.fullname" . }} +{{- if .Values.controller.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.controller.metrics.serviceMonitor.namespace | quote }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.metrics.serviceMonitor.additionalLabels }} + {{- toYaml .Values.controller.metrics.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: metrics + interval: {{ .Values.controller.metrics.serviceMonitor.scrapeInterval }} + {{- if .Values.controller.metrics.serviceMonitor.honorLabels }} + honorLabels: true + {{- end }} + {{- if .Values.controller.metrics.serviceMonitor.relabelings }} + relabelings: {{ toYaml .Values.controller.metrics.serviceMonitor.relabelings | nindent 8 }} + {{- end }} + {{- if .Values.controller.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{ toYaml .Values.controller.metrics.serviceMonitor.metricRelabelings | nindent 8 }} + {{- end }} +{{- if .Values.controller.metrics.serviceMonitor.jobLabel }} + jobLabel: {{ .Values.controller.metrics.serviceMonitor.jobLabel | quote }} +{{- end }} +{{- if .Values.controller.metrics.serviceMonitor.namespaceSelector }} + namespaceSelector: {{ toYaml .Values.controller.metrics.serviceMonitor.namespaceSelector | nindent 4 }} +{{- else }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{- end }} +{{- if .Values.controller.metrics.serviceMonitor.targetLabels }} + targetLabels: + {{- range .Values.controller.metrics.serviceMonitor.targetLabels }} + - {{ . }} + {{- end }} +{{- end }} + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-deployment.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-deployment.yaml new file mode 100644 index 000000000..fd3e96e9e --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-deployment.yaml @@ -0,0 +1,118 @@ +{{- if .Values.defaultBackend.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: default-backend +{{- if not .Values.defaultBackend.autoscaling.enabled }} + replicas: {{ .Values.defaultBackend.replicaCount }} +{{- end }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + {{- if .Values.defaultBackend.podAnnotations }} + annotations: {{ toYaml .Values.defaultBackend.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.defaultBackend.podLabels }} + {{- toYaml .Values.defaultBackend.podLabels | nindent 8 }} + {{- end }} + spec: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + {{- if .Values.defaultBackend.priorityClassName }} + priorityClassName: {{ .Values.defaultBackend.priorityClassName }} + {{- end }} + {{- if .Values.defaultBackend.podSecurityContext }} + securityContext: {{ toYaml .Values.defaultBackend.podSecurityContext | nindent 8 }} + {{- end }} + containers: + - name: {{ template "ingress-nginx.name" . }}-default-backend + {{- with .Values.defaultBackend.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.defaultBackend.image.pullPolicy }} + {{- if .Values.defaultBackend.extraArgs }} + args: + {{- range $key, $value := .Values.defaultBackend.extraArgs }} + {{- /* Accept keys without values or with false as value */}} + {{- if eq ($value | quote | len) 2 }} + - --{{ $key }} + {{- else }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- end }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + runAsUser: {{ .Values.defaultBackend.image.runAsUser }} + runAsNonRoot: {{ .Values.defaultBackend.image.runAsNonRoot }} + allowPrivilegeEscalation: {{ .Values.defaultBackend.image.allowPrivilegeEscalation }} + readOnlyRootFilesystem: {{ .Values.defaultBackend.image.readOnlyRootFilesystem}} + {{- if .Values.defaultBackend.extraEnvs }} + env: {{ toYaml .Values.defaultBackend.extraEnvs | nindent 12 }} + {{- end }} + livenessProbe: + httpGet: + path: /healthz + port: {{ .Values.defaultBackend.port }} + scheme: HTTP + initialDelaySeconds: {{ .Values.defaultBackend.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.defaultBackend.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.defaultBackend.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.defaultBackend.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.defaultBackend.livenessProbe.failureThreshold }} + readinessProbe: + httpGet: + path: /healthz + port: {{ .Values.defaultBackend.port }} + scheme: HTTP + initialDelaySeconds: {{ .Values.defaultBackend.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.defaultBackend.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.defaultBackend.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.defaultBackend.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.defaultBackend.readinessProbe.failureThreshold }} + ports: + - name: http + containerPort: {{ .Values.defaultBackend.port }} + protocol: TCP + {{- if .Values.defaultBackend.extraVolumeMounts }} + volumeMounts: {{- toYaml .Values.defaultBackend.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- if .Values.defaultBackend.resources }} + resources: {{ toYaml .Values.defaultBackend.resources | nindent 12 }} + {{- end }} + {{- if .Values.defaultBackend.nodeSelector }} + nodeSelector: {{ toYaml .Values.defaultBackend.nodeSelector | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} + {{- if .Values.defaultBackend.tolerations }} + tolerations: {{ toYaml .Values.defaultBackend.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.defaultBackend.affinity }} + affinity: {{ toYaml .Values.defaultBackend.affinity | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: 60 + {{- if .Values.defaultBackend.extraVolumes }} + volumes: {{ toYaml .Values.defaultBackend.extraVolumes | nindent 8 }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-hpa.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-hpa.yaml new file mode 100644 index 000000000..594d26525 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-hpa.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.defaultBackend.enabled .Values.defaultBackend.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ template "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "ingress-nginx.defaultBackend.fullname" . }} + minReplicas: {{ .Values.defaultBackend.autoscaling.minReplicas }} + maxReplicas: {{ .Values.defaultBackend.autoscaling.maxReplicas }} + metrics: +{{- with .Values.defaultBackend.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ . }} +{{- end }} +{{- with .Values.defaultBackend.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ . }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml new file mode 100644 index 000000000..00891cee5 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml @@ -0,0 +1,21 @@ +{{- if .Values.defaultBackend.enabled -}} +{{- if or (gt (.Values.defaultBackend.replicaCount | int) 1) (gt (.Values.defaultBackend.autoscaling.minReplicas | int) 1) }} +apiVersion: {{ ternary "policy/v1" "policy/v1beta1" (semverCompare ">=1.21.0-0" .Capabilities.KubeVersion.Version) }} +kind: PodDisruptionBudget +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: default-backend + minAvailable: {{ .Values.defaultBackend.minAvailable }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-psp.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-psp.yaml new file mode 100644 index 000000000..42061c5d3 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-psp.yaml @@ -0,0 +1,36 @@ +{{- if and .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled (empty .Values.defaultBackend.existingPsp) -}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "ingress-nginx.fullname" . }}-backend + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + allowPrivilegeEscalation: false + fsGroup: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + requiredDropCapabilities: + - ALL + runAsUser: + rule: MustRunAsNonRoot + seLinux: + rule: RunAsAny + supplementalGroups: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-role.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-role.yaml new file mode 100644 index 000000000..a2b457c36 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-role.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.rbac.create .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }}-backend + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: [{{ template "podSecurityPolicy.apiGroup" . }}] + resources: ['podsecuritypolicies'] + verbs: ['use'] + {{- with .Values.defaultBackend.existingPsp }} + resourceNames: [{{ . }}] + {{- else }} + resourceNames: [{{ include "ingress-nginx.fullname" . }}-backend] + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-rolebinding.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-rolebinding.yaml new file mode 100644 index 000000000..dbaa516b9 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.rbac.create .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.fullname" . }}-backend + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "ingress-nginx.fullname" . }}-backend +subjects: + - kind: ServiceAccount + name: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-service.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-service.yaml new file mode 100644 index 000000000..5f1d09a95 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-service.yaml @@ -0,0 +1,41 @@ +{{- if .Values.defaultBackend.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.defaultBackend.service.annotations }} + annotations: {{ toYaml .Values.defaultBackend.service.annotations | nindent 4 }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.defaultBackend.service.type }} +{{- if .Values.defaultBackend.service.clusterIP }} + clusterIP: {{ .Values.defaultBackend.service.clusterIP }} +{{- end }} +{{- if .Values.defaultBackend.service.externalIPs }} + externalIPs: {{ toYaml .Values.defaultBackend.service.externalIPs | nindent 4 }} +{{- end }} +{{- if .Values.defaultBackend.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.defaultBackend.service.loadBalancerIP }} +{{- end }} +{{- if .Values.defaultBackend.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.defaultBackend.service.loadBalancerSourceRanges | nindent 4 }} +{{- end }} + ports: + - name: http + port: {{ .Values.defaultBackend.service.servicePort }} + protocol: TCP + targetPort: http + {{- if semverCompare ">=1.20" .Capabilities.KubeVersion.Version }} + appProtocol: http + {{- end }} + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: default-backend +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml new file mode 100644 index 000000000..b45a95ad2 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.defaultBackend.enabled .Values.defaultBackend.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + {{- with .Values.defaultBackend.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +automountServiceAccountToken: {{ .Values.defaultBackend.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/dh-param-secret.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/dh-param-secret.yaml new file mode 100644 index 000000000..12e7a4f63 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/templates/dh-param-secret.yaml @@ -0,0 +1,10 @@ +{{- with .Values.dhParam -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "ingress-nginx.controller.fullname" $ }} + labels: + {{- include "ingress-nginx.labels" $ | nindent 4 }} +data: + dhparam.pem: {{ . }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/ingress-nginx/values.yaml b/scripts/helmcharts/openreplay/charts/ingress-nginx/values.yaml new file mode 100644 index 000000000..c887dc051 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/ingress-nginx/values.yaml @@ -0,0 +1,919 @@ +## nginx configuration +## Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/index.md +## + +## Overrides for generated resource names +# See templates/_helpers.tpl +# nameOverride: +# fullnameOverride: + +## Labels to apply to all resources +## +commonLabels: {} +# scmhash: abc123 +# myLabel: aakkmd + +controller: + name: controller + image: + registry: k8s.gcr.io + image: ingress-nginx/controller + ## for backwards compatibility consider setting the full image url via the repository value below + ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail + ## repository: + tag: "v1.1.2" + digest: sha256:28b11ce69e57843de44e3db6413e98d09de0f6688e33d4bd384002a44f78405c + pullPolicy: IfNotPresent + # www-data -> uid 101 + runAsUser: 101 + allowPrivilegeEscalation: true + + # -- Use an existing PSP instead of creating one + existingPsp: "" + + # -- Configures the controller container name + containerName: controller + + # -- Configures the ports that the nginx-controller listens on + containerPort: + http: 80 + https: 443 + + # -- Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/ + config: {} + + # -- Annotations to be added to the controller config configuration configmap. + configAnnotations: {} + + # -- Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/main/docs/examples/customization/custom-headers + proxySetHeaders: {} + + # -- Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headers + addHeaders: {} + + # -- Optionally customize the pod dnsConfig. + dnsConfig: {} + + # -- Optionally customize the pod hostname. + hostname: {} + + # -- Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'. + # By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller + # to keep resolving names inside the k8s network, use ClusterFirstWithHostNet. + dnsPolicy: ClusterFirst + + # -- Bare-metal considerations via the host network https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network + # Ingress status was blank because there is no Service exposing the NGINX Ingress controller in a configuration using the host network, the default --publish-service flag used in standard cloud setups does not apply + reportNodeInternalIp: false + + # -- Process Ingress objects without ingressClass annotation/ingressClassName field + # Overrides value for --watch-ingress-without-class flag of the controller binary + # Defaults to false + watchIngressWithoutClass: false + + # -- Process IngressClass per name (additionally as per spec.controller). + ingressClassByName: false + + # -- This configuration defines if Ingress Controller should allow users to set + # their own *-snippet annotations, otherwise this is forbidden / dropped + # when users add those annotations. + # Global snippets in ConfigMap are still respected + allowSnippetAnnotations: true + + # -- Required for use with CNI based kubernetes installations (such as ones set up by kubeadm), + # since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920 + # is merged + hostNetwork: false + + ## Use host ports 80 and 443 + ## Disabled by default + hostPort: + # -- Enable 'hostPort' or not + enabled: false + ports: + # -- 'hostPort' http port + http: 80 + # -- 'hostPort' https port + https: 443 + + # -- Election ID to use for status update + electionID: ingress-controller-leader + + ## This section refers to the creation of the IngressClass resource + ## IngressClass resources are supported since k8s >= 1.18 and required since k8s >= 1.19 + ingressClassResource: + # -- Name of the ingressClass + name: nginx + # -- Is this ingressClass enabled or not + enabled: true + # -- Is this the default ingressClass for the cluster + default: false + # -- Controller-value of the controller that is processing this ingressClass + controllerValue: "k8s.io/ingress-nginx" + + # -- Parameters is a link to a custom resource containing additional + # configuration for the controller. This is optional if the controller + # does not require extra parameters. + parameters: {} + + # -- For backwards compatibility with ingress.class annotation, use ingressClass. + # Algorithm is as follows, first ingressClassName is considered, if not present, controller looks for ingress.class annotation + ingressClass: nginx + + # -- Labels to add to the pod container metadata + podLabels: {} + # key: value + + # -- Security Context policies for controller pods + podSecurityContext: {} + + # -- See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for notes on enabling and using sysctls + sysctls: {} + # sysctls: + # "net.core.somaxconn": "8192" + + # -- Allows customization of the source of the IP address or FQDN to report + # in the ingress status field. By default, it reads the information provided + # by the service. If disable, the status field reports the IP address of the + # node or nodes where an ingress controller pod is running. + publishService: + # -- Enable 'publishService' or not + enabled: true + # -- Allows overriding of the publish service to bind to + # Must be / + pathOverride: "" + + # Limit the scope of the controller to a specific namespace + scope: + # -- Enable 'scope' or not + enabled: false + # -- Namespace to limit the controller to; defaults to $(POD_NAMESPACE) + namespace: "" + # -- When scope.enabled == false, instead of watching all namespaces, we watching namespaces whose labels + # only match with namespaceSelector. Format like foo=bar. Defaults to empty, means watching all namespaces. + namespaceSelector: "" + + # -- Allows customization of the configmap / nginx-configmap namespace; defaults to $(POD_NAMESPACE) + configMapNamespace: "" + + tcp: + # -- Allows customization of the tcp-services-configmap; defaults to $(POD_NAMESPACE) + configMapNamespace: "" + # -- Annotations to be added to the tcp config configmap + annotations: {} + + udp: + # -- Allows customization of the udp-services-configmap; defaults to $(POD_NAMESPACE) + configMapNamespace: "" + # -- Annotations to be added to the udp config configmap + annotations: {} + + # -- Maxmind license key to download GeoLite2 Databases. + ## https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases + maxmindLicenseKey: "" + + # -- Additional command line arguments to pass to nginx-ingress-controller + # E.g. to specify the default SSL certificate you can use + extraArgs: {} + ## extraArgs: + ## default-ssl-certificate: "/" + + # -- Additional environment variables to set + extraEnvs: [] + # extraEnvs: + # - name: FOO + # valueFrom: + # secretKeyRef: + # key: FOO + # name: secret-resource + + # -- Use a `DaemonSet` or `Deployment` + kind: Deployment + + # -- Annotations to be added to the controller Deployment or DaemonSet + ## + annotations: {} + # keel.sh/pollSchedule: "@every 60m" + + # -- Labels to be added to the controller Deployment or DaemonSet and other resources that do not have option to specify labels + ## + labels: {} + # keel.sh/policy: patch + # keel.sh/trigger: poll + + + # -- The update strategy to apply to the Deployment or DaemonSet + ## + updateStrategy: {} + # rollingUpdate: + # maxUnavailable: 1 + # type: RollingUpdate + + # -- `minReadySeconds` to avoid killing pods before we are ready + ## + minReadySeconds: 0 + + + # -- Node tolerations for server scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + # -- Affinity and anti-affinity rules for server scheduling to nodes + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## + affinity: {} + # # An example of preferred pod anti-affinity, weight is in the range 1-100 + # podAntiAffinity: + # preferredDuringSchedulingIgnoredDuringExecution: + # - weight: 100 + # podAffinityTerm: + # labelSelector: + # matchExpressions: + # - key: app.kubernetes.io/name + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/instance + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/component + # operator: In + # values: + # - controller + # topologyKey: kubernetes.io/hostname + + # # An example of required pod anti-affinity + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchExpressions: + # - key: app.kubernetes.io/name + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/instance + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/component + # operator: In + # values: + # - controller + # topologyKey: "kubernetes.io/hostname" + + # -- Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in. + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + ## + topologySpreadConstraints: [] + # - maxSkew: 1 + # topologyKey: failure-domain.beta.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app.kubernetes.io/instance: ingress-nginx-internal + + # -- `terminationGracePeriodSeconds` to avoid killing pods before we are ready + ## wait up to five minutes for the drain of connections + ## + terminationGracePeriodSeconds: 300 + + # -- Node labels for controller pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: + kubernetes.io/os: linux + + ## Liveness and readiness probe values + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes + ## + ## startupProbe: + ## httpGet: + ## # should match container.healthCheckPath + ## path: "/healthz" + ## port: 10254 + ## scheme: HTTP + ## initialDelaySeconds: 5 + ## periodSeconds: 5 + ## timeoutSeconds: 2 + ## successThreshold: 1 + ## failureThreshold: 5 + livenessProbe: + httpGet: + # should match container.healthCheckPath + path: "/healthz" + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + httpGet: + # should match container.healthCheckPath + path: "/healthz" + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + + + # -- Path of the health check endpoint. All requests received on the port defined by + # the healthz-port parameter are forwarded internally to this path. + healthCheckPath: "/healthz" + + # -- Address to bind the health check endpoint. + # It is better to set this option to the internal node address + # if the ingress nginx controller is running in the `hostNetwork: true` mode. + healthCheckHost: "" + + # -- Annotations to be added to controller pods + ## + podAnnotations: {} + + replicaCount: 1 + + minAvailable: 1 + + ## Define requests resources to avoid probe issues due to CPU utilization in busy nodes + ## ref: https://github.com/kubernetes/ingress-nginx/issues/4735#issuecomment-551204903 + ## Ideally, there should be no limits. + ## https://engineering.indeedblog.com/blog/2019/12/cpu-throttling-regression-fix/ + resources: + ## limits: + ## cpu: 100m + ## memory: 90Mi + requests: + cpu: 100m + memory: 90Mi + + # Mutually exclusive with keda autoscaling + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 11 + targetCPUUtilizationPercentage: 50 + targetMemoryUtilizationPercentage: 50 + behavior: {} + # scaleDown: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 1 + # periodSeconds: 180 + # scaleUp: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 2 + # periodSeconds: 60 + + autoscalingTemplate: [] + # Custom or additional autoscaling metrics + # ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics + # - type: Pods + # pods: + # metric: + # name: nginx_ingress_controller_nginx_process_requests_total + # target: + # type: AverageValue + # averageValue: 10000m + + # Mutually exclusive with hpa autoscaling + keda: + apiVersion: "keda.sh/v1alpha1" + ## apiVersion changes with keda 1.x vs 2.x + ## 2.x = keda.sh/v1alpha1 + ## 1.x = keda.k8s.io/v1alpha1 + enabled: false + minReplicas: 1 + maxReplicas: 11 + pollingInterval: 30 + cooldownPeriod: 300 + restoreToOriginalReplicaCount: false + scaledObject: + annotations: {} + # Custom annotations for ScaledObject resource + # annotations: + # key: value + triggers: [] + # - type: prometheus + # metadata: + # serverAddress: http://:9090 + # metricName: http_requests_total + # threshold: '100' + # query: sum(rate(http_requests_total{deployment="my-deployment"}[2m])) + + behavior: {} + # scaleDown: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 1 + # periodSeconds: 180 + # scaleUp: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 2 + # periodSeconds: 60 + + # -- Enable mimalloc as a drop-in replacement for malloc. + ## ref: https://github.com/microsoft/mimalloc + ## + enableMimalloc: true + + ## Override NGINX template + customTemplate: + configMapName: "" + configMapKey: "" + + service: + enabled: true + + # -- If enabled is adding an appProtocol option for Kubernetes service. An appProtocol field replacing annotations that were + # using for setting a backend protocol. Here is an example for AWS: service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http + # It allows choosing the protocol for each backend specified in the Kubernetes service. + # See the following GitHub issue for more details about the purpose: https://github.com/kubernetes/kubernetes/issues/40244 + # Will be ignored for Kubernetes versions older than 1.20 + ## + appProtocol: true + + annotations: {} + labels: {} + # clusterIP: "" + + # -- List of IP addresses at which the controller services are available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + + enableHttp: true + enableHttps: true + + ## Set external traffic policy to: "Local" to preserve source IP on providers supporting it. + ## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer + # externalTrafficPolicy: "" + + ## Must be either "None" or "ClientIP" if set. Kubernetes will default to "None". + ## Ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + # sessionAffinity: "" + + ## Specifies the health check node port (numeric port number) for the service. If healthCheckNodePort isn’t specified, + ## the service controller allocates a port from your cluster’s NodePort range. + ## Ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + # healthCheckNodePort: 0 + + # -- Represents the dual-stack-ness requested or required by this Service. Possible values are + # SingleStack, PreferDualStack or RequireDualStack. + # The ipFamilies and clusterIPs fields depend on the value of this field. + ## Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/ + ipFamilyPolicy: "SingleStack" + + # -- List of IP families (e.g. IPv4, IPv6) assigned to the service. This field is usually assigned automatically + # based on cluster configuration and the ipFamilyPolicy field. + ## Ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/ + ipFamilies: + - IPv4 + + ports: + http: 80 + https: 443 + + targetPorts: + http: http + https: https + + type: LoadBalancer + + ## type: NodePort + ## nodePorts: + ## http: 32080 + ## https: 32443 + ## tcp: + ## 8080: 32808 + nodePorts: + http: "" + https: "" + tcp: {} + udp: {} + + external: + enabled: true + + internal: + # -- Enables an additional internal load balancer (besides the external one). + enabled: false + # -- Annotations are mandatory for the load balancer to come up. Varies with the cloud service. + annotations: {} + + # loadBalancerIP: "" + + # -- Restrict access For LoadBalancer service. Defaults to 0.0.0.0/0. + loadBalancerSourceRanges: [] + + ## Set external traffic policy to: "Local" to preserve source IP on + ## providers supporting it + ## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer + # externalTrafficPolicy: "" + + # -- Additional containers to be added to the controller pod. + # See https://github.com/lemonldap-ng-controller/lemonldap-ng-controller as example. + extraContainers: [] + # - name: my-sidecar + # image: nginx:latest + # - name: lemonldap-ng-controller + # image: lemonldapng/lemonldap-ng-controller:0.2.0 + # args: + # - /lemonldap-ng-controller + # - --alsologtostderr + # - --configmap=$(POD_NAMESPACE)/lemonldap-ng-configuration + # env: + # - name: POD_NAME + # valueFrom: + # fieldRef: + # fieldPath: metadata.name + # - name: POD_NAMESPACE + # valueFrom: + # fieldRef: + # fieldPath: metadata.namespace + # volumeMounts: + # - name: copy-portal-skins + # mountPath: /srv/var/lib/lemonldap-ng/portal/skins + + # -- Additional volumeMounts to the controller main container. + extraVolumeMounts: [] + # - name: copy-portal-skins + # mountPath: /var/lib/lemonldap-ng/portal/skins + + # -- Additional volumes to the controller pod. + extraVolumes: [] + # - name: copy-portal-skins + # emptyDir: {} + + # -- Containers, which are run before the app containers are started. + extraInitContainers: [] + # - name: init-myservice + # image: busybox + # command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] + + extraModules: [] + ## Modules, which are mounted into the core nginx image + # - name: opentelemetry + # image: busybox + # + # The image must contain a `/usr/local/bin/init_module.sh` executable, which + # will be executed as initContainers, to move its config files within the + # mounted volume. + + admissionWebhooks: + annotations: {} + # ignore-check.kube-linter.io/no-read-only-rootfs: "This deployment needs write access to root filesystem". + + ## Additional annotations to the admission webhooks. + ## These annotations will be added to the ValidatingWebhookConfiguration and + ## the Jobs Spec of the admission webhooks. + enabled: true + failurePolicy: Fail + # timeoutSeconds: 10 + port: 8443 + certificate: "/usr/local/certificates/cert" + key: "/usr/local/certificates/key" + namespaceSelector: {} + objectSelector: {} + # -- Labels to be added to admission webhooks + labels: {} + + # -- Use an existing PSP instead of creating one + existingPsp: "" + + service: + annotations: {} + # clusterIP: "" + externalIPs: [] + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 443 + type: ClusterIP + + createSecretJob: + resources: {} + # limits: + # cpu: 10m + # memory: 20Mi + # requests: + # cpu: 10m + # memory: 20Mi + + patchWebhookJob: + resources: {} + + patch: + enabled: true + image: + registry: k8s.gcr.io + image: ingress-nginx/kube-webhook-certgen + ## for backwards compatibility consider setting the full image url via the repository value below + ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail + ## repository: + tag: v1.1.1 + digest: sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660 + pullPolicy: IfNotPresent + # -- Provide a priority class name to the webhook patching job + ## + priorityClassName: "" + podAnnotations: {} + nodeSelector: + kubernetes.io/os: linux + tolerations: [] + # -- Labels to be added to patch job resources + labels: {} + runAsUser: 2000 + fsGroup: 2000 + + metrics: + port: 10254 + # if this port is changed, change healthz-port: in extraArgs: accordingly + enabled: false + + service: + annotations: {} + # prometheus.io/scrape: "true" + # prometheus.io/port: "10254" + + # clusterIP: "" + + # -- List of IP addresses at which the stats-exporter service is available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 10254 + type: ClusterIP + # externalTrafficPolicy: "" + # nodePort: "" + + serviceMonitor: + enabled: false + additionalLabels: {} + ## The label to use to retrieve the job name from. + ## jobLabel: "app.kubernetes.io/name" + namespace: "" + namespaceSelector: {} + ## Default: scrape .Release.Namespace only + ## To scrape all, use the following: + ## namespaceSelector: + ## any: true + scrapeInterval: 30s + # honorLabels: true + targetLabels: [] + relabelings: [] + metricRelabelings: [] + + prometheusRule: + enabled: false + additionalLabels: {} + # namespace: "" + rules: [] + # # These are just examples rules, please adapt them to your needs + # - alert: NGINXConfigFailed + # expr: count(nginx_ingress_controller_config_last_reload_successful == 0) > 0 + # for: 1s + # labels: + # severity: critical + # annotations: + # description: bad ingress config - nginx config test failed + # summary: uninstall the latest ingress changes to allow config reloads to resume + # - alert: NGINXCertificateExpiry + # expr: (avg(nginx_ingress_controller_ssl_expire_time_seconds) by (host) - time()) < 604800 + # for: 1s + # labels: + # severity: critical + # annotations: + # description: ssl certificate(s) will expire in less then a week + # summary: renew expiring certificates to avoid downtime + # - alert: NGINXTooMany500s + # expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"5.+"} ) / sum(nginx_ingress_controller_requests) ) > 5 + # for: 1m + # labels: + # severity: warning + # annotations: + # description: Too many 5XXs + # summary: More than 5% of all requests returned 5XX, this requires your attention + # - alert: NGINXTooMany400s + # expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"4.+"} ) / sum(nginx_ingress_controller_requests) ) > 5 + # for: 1m + # labels: + # severity: warning + # annotations: + # description: Too many 4XXs + # summary: More than 5% of all requests returned 4XX, this requires your attention + + # -- Improve connection draining when ingress controller pod is deleted using a lifecycle hook: + # With this new hook, we increased the default terminationGracePeriodSeconds from 30 seconds + # to 300, allowing the draining of connections up to five minutes. + # If the active connections end before that, the pod will terminate gracefully at that time. + # To effectively take advantage of this feature, the Configmap feature + # worker-shutdown-timeout new value is 240s instead of 10s. + ## + lifecycle: + preStop: + exec: + command: + - /wait-shutdown + + priorityClassName: "" + +# -- Rollback limit +## +revisionHistoryLimit: 10 + +## Default 404 backend +## +defaultBackend: + ## + enabled: false + + name: defaultbackend + image: + registry: k8s.gcr.io + image: defaultbackend-amd64 + ## for backwards compatibility consider setting the full image url via the repository value below + ## use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail + ## repository: + tag: "1.5" + pullPolicy: IfNotPresent + # nobody user -> uid 65534 + runAsUser: 65534 + runAsNonRoot: true + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + + # -- Use an existing PSP instead of creating one + existingPsp: "" + + extraArgs: {} + + serviceAccount: + create: true + name: "" + automountServiceAccountToken: true + # -- Additional environment variables to set for defaultBackend pods + extraEnvs: [] + + port: 8080 + + ## Readiness and liveness probes for default backend + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/ + ## + livenessProbe: + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + failureThreshold: 6 + initialDelaySeconds: 0 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + + # -- Node tolerations for server scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + affinity: {} + + # -- Security Context policies for controller pods + # See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for + # notes on enabling and using sysctls + ## + podSecurityContext: {} + + # -- Security Context policies for controller main container. + # See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for + # notes on enabling and using sysctls + ## + containerSecurityContext: {} + + # -- Labels to add to the pod container metadata + podLabels: {} + # key: value + + # -- Node labels for default backend pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: + kubernetes.io/os: linux + + # -- Annotations to be added to default backend pods + ## + podAnnotations: {} + + replicaCount: 1 + + minAvailable: 1 + + resources: {} + # limits: + # cpu: 10m + # memory: 20Mi + # requests: + # cpu: 10m + # memory: 20Mi + + extraVolumeMounts: [] + ## Additional volumeMounts to the default backend container. + # - name: copy-portal-skins + # mountPath: /var/lib/lemonldap-ng/portal/skins + + extraVolumes: [] + ## Additional volumes to the default backend pod. + # - name: copy-portal-skins + # emptyDir: {} + + autoscaling: + annotations: {} + enabled: false + minReplicas: 1 + maxReplicas: 2 + targetCPUUtilizationPercentage: 50 + targetMemoryUtilizationPercentage: 50 + + service: + annotations: {} + + # clusterIP: "" + + # -- List of IP addresses at which the default backend service is available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 80 + type: ClusterIP + + priorityClassName: "" + # -- Labels to be added to the default backend resources + labels: {} + +## Enable RBAC as per https://github.com/kubernetes/ingress-nginx/blob/main/docs/deploy/rbac.md and https://github.com/kubernetes/ingress-nginx/issues/266 +rbac: + create: true + scope: false + +## If true, create & use Pod Security Policy resources +## https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +podSecurityPolicy: + enabled: false + +serviceAccount: + create: true + name: "" + automountServiceAccountToken: true + # -- Annotations for the controller service account + annotations: {} + +# -- Optional array of imagePullSecrets containing private registry credentials +## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ +imagePullSecrets: [] +# - name: secretName + +# -- TCP service key:value pairs +## Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/exposing-tcp-udp-services.md +## +tcp: {} +# 8080: "default/example-tcp-svc:9000" + +# -- UDP service key:value pairs +## Ref: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/exposing-tcp-udp-services.md +## +udp: {} +# 53: "kube-system/kube-dns:53" + +# -- (string) A base64-encoded Diffie-Hellman parameter. +# This can be generated with: `openssl dhparam 4096 2> /dev/null | base64` +## Ref: https://github.com/kubernetes/ingress-nginx/tree/main/docs/examples/customization/ssl-dh-param +dhParam: diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/Chart.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/Chart.yaml deleted file mode 100644 index 8aa1dcf82..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: nginx-ingress -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -AppVersion: "v1.5.4" diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/files/site.crt b/scripts/helmcharts/openreplay/charts/nginx-ingress/files/site.crt deleted file mode 120000 index 12e23824a..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/files/site.crt +++ /dev/null @@ -1 +0,0 @@ -../../../files/site.crt \ No newline at end of file diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/files/site.key b/scripts/helmcharts/openreplay/charts/nginx-ingress/files/site.key deleted file mode 120000 index 3805a27d1..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/files/site.key +++ /dev/null @@ -1 +0,0 @@ -../../../files/site.key \ No newline at end of file diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/NOTES.txt b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/NOTES.txt deleted file mode 100644 index 8125afe9c..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/NOTES.txt +++ /dev/null @@ -1,22 +0,0 @@ -1. Get the application URL by running these commands: -{{- if .Values.ingress.enabled }} -{{- range $host := .Values.ingress.hosts }} - {{- range .paths }} - http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} - {{- end }} -{{- end }} -{{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "nginx-ingress.fullname" . }}) - export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT -{{- else if contains "LoadBalancer" .Values.service.type }} - NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "nginx-ingress.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "nginx-ingress.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") - echo http://$SERVICE_IP:{{ .Values.service.port }} -{{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "nginx-ingress.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") - export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT -{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/_helpers.tpl b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/_helpers.tpl deleted file mode 100644 index b0f2808f1..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/_helpers.tpl +++ /dev/null @@ -1,62 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "nginx-ingress.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "nginx-ingress.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "nginx-ingress.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "nginx-ingress.labels" -}} -helm.sh/chart: {{ include "nginx-ingress.chart" . }} -{{ include "nginx-ingress.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "nginx-ingress.selectorLabels" -}} -app.kubernetes.io/name: {{ include "nginx-ingress.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "nginx-ingress.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "nginx-ingress.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/configMap.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/configMap.yaml deleted file mode 100644 index 6322318e3..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/configMap.yaml +++ /dev/null @@ -1,190 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: nginx - namespace: {{ .Release.Namespace }} -data: - location.list: |- - location ~* /general_stats { - deny all; - } - location /healthz { - return 200 'OK'; - } - location ~ ^/(mobs|sessions-assets|frontend|static|sourcemaps|ios-images)/ { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $origin_forwarded_ip; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header Host $http_host; - - proxy_connect_timeout 300; - # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 - proxy_http_version 1.1; - proxy_set_header Connection ""; - chunked_transfer_encoding off; - - proxy_pass http://minio.db.svc.cluster.local:9000; - } - - location /minio/ { - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_pass http://minio.db.svc.cluster.local:9000; - } - location /ingest/ { - rewrite ^/ingest/(.*) /$1 break; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header X-Forwarded-For $origin_forwarded_ip; - proxy_set_header X-Forwarded-Host $origin_forwarded_ip; - proxy_set_header X-Real-IP $origin_forwarded_ip; - proxy_set_header Host $host; - proxy_pass http://http-openreplay.app.svc.cluster.local; - proxy_read_timeout 300; - proxy_connect_timeout 120; - proxy_send_timeout 300; - } - location /grafana { - set $target http://monitoring-grafana.monitoring.svc.cluster.local; - rewrite ^/grafana/(.*) /$1 break; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_pass $target; - } - location /api/ { - rewrite ^/api/(.*) /$1 break; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-Proto $origin_proto; - proxy_pass http://chalice-openreplay.app.svc.cluster.local:8000; - } - location /assist/ { - rewrite ^/assist/(.*) /$1 break; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $origin_forwarded_ip; - proxy_pass http://utilities-openreplay.app.svc.cluster.local:9000; - } - location /ws-assist/ { - rewrite ^/ws-assist/(.*) /$1 break; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Forwarded-For $origin_forwarded_ip; - proxy_set_header X-Real-IP $origin_forwarded_ip; - proxy_pass http://utilities-openreplay.app.svc.cluster.local:9001; - } - location /assets/ { - rewrite ^/assets/(.*) /sessions-assets/$1 break; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_pass http://minio.db.svc.cluster.local:9000; - } - location / { - index /index.html; - rewrite ^((?!.(js|css|png|svg|jpg|woff|woff2)).)*$ /frontend/index.html break; - proxy_http_version 1.1; - proxy_set_header Connection ""; - include /etc/nginx/conf.d/compression.conf; - proxy_set_header Host $http_host; - proxy_pass http://minio.db.svc.cluster.local:9000/frontend/; - proxy_intercept_errors on; # see http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors - error_page 404 =200 /index.html; - } - compression.conf: |- - # Compression - gzip on; - gzip_comp_level 5; - gzip_min_length 256; # 256Bytes - gzip_proxied any; - gzip_vary on; - # Content types for compression - gzip_types - application/atom+xml - application/javascript - application/json - application/ld+json - application/manifest+json - application/rss+xml - application/vnd.geo+json - application/vnd.ms-fontobject - application/x-font-ttf - application/x-web-app-manifest+json - application/xhtml+xml - application/xml - font/opentype - image/bmp - image/svg+xml - image/x-icon - text/cache-manifest - text/css - text/plain - ; - - sites.conf: |- - # Ref: https://github.com/openresty/openresty/#resolvconf-parsing - resolver local=on; - # Need real ip address for flags in replay. - # Some LBs will forward real ips as x-forwarded-for - # So making that as priority - map $http_x_forwarded_for $origin_forwarded_ip { - ~^(\d+\.\d+\.\d+\.\d+) $1; - default $remote_addr; - } - map $http_upgrade $connection_upgrade { - default upgrade; - '' close; - } - map $http_x_forwarded_proto $origin_proto { - default $http_x_forwarded_proto; - '' $scheme; - } - # Default server for helath check - server { - listen 80 default_server; - listen [::]:80; - location /healthz { - return 200 'OK'; - } - } - - upstream utilities-pool { - # comment this line if you have multiple pods - server utilities-openreplay-headless.app:9001; - # uncomment and change these lines if you have multiple pods - # server POD1_IP:9001; - # server POD2_IP:9001; - hash $origin_forwarded_ip consistent; - } - - server { - listen 80; - listen [::]:80; - server_name {{ .Values.global.domainName }}; - {{ .Values.customServerConfigs }} - include /etc/nginx/conf.d/location.list; - client_max_body_size 10M; - } - server { - listen 443 ssl; - server_name {{ .Values.global.domainName }}; - ssl_certificate /etc/secrets/site.crt; - ssl_certificate_key /etc/secrets/site.key; - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA HIGH !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"; - include /etc/nginx/conf.d/location.list; - client_max_body_size 10M; - } - diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/deployment.yaml deleted file mode 100644 index 95c05aa67..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/deployment.yaml +++ /dev/null @@ -1,80 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "nginx-ingress.fullname" . }} - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include "nginx-ingress.selectorLabels" . | nindent 6 }} - template: - metadata: - annotations: - nginxRolloutID: {{ randAlphaNum 5 | quote }} # Restart nginx after every deployment - {{- with .Values.podAnnotations }} - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "nginx-ingress.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "nginx-ingress.serviceAccountName" . }} - securityContext: - {{- toYaml .Values.podSecurityContext | nindent 8 }} - containers: - - name: {{ .Chart.Name }} - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - name: https - containerPort: 443 - protocol: TCP - - name: http - containerPort: 80 - protocol: TCP - - name: metrics - containerPort: 9145 - protocol: TCP - livenessProbe: - httpGet: - path: /healthz - port: http - readinessProbe: - httpGet: - path: /healthz - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - volumeMounts: - - name: nginx - mountPath: /etc/nginx/conf.d/ - - name: ssl - mountPath: /etc/secrets/ - volumes: - - name: nginx - configMap: - name: nginx - - name: ssl - secret: - secretName: ssl - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/hpa.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/hpa.yaml deleted file mode 100644 index 348f8f95b..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/hpa.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "nginx-ingress.fullname" . }} - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "nginx-ingress.fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/ingress.yaml deleted file mode 100644 index 63cfce077..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/ingress.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if .Values.ingress.enabled -}} -{{- $fullName := include "nginx-ingress.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} - tls: - {{- range .Values.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/secrets.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/secrets.yaml deleted file mode 100644 index 91b7cc09c..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/secrets.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -apiVersion: v1 -kind: Secret -metadata: - name: ssl -data: - ca.crt: '' - site.crt: '{{ .Files.Get "files/site.crt" | b64enc }}' - site.key: '{{ .Files.Get "files/site.key" | b64enc }}' diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/service.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/service.yaml deleted file mode 100644 index f20d4fc38..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/service.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "nginx-ingress.fullname" . }} - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} - annotations: - {{- with .Values.service.annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - type: {{ .Values.service.type }} - {{- if or (eq .Values.service.type "LoadBalancer") (eq .Values.service.type "NodePort")}} - # Make sure to get client ip - externalTrafficPolicy: Local - {{- end}} - ports: - {{- range .Values.service.ports }} - - port: {{ .port }} - targetPort: {{ .targetPort }} - protocol: TCP - name: {{ .name }} - {{- end }} - selector: - {{- include "nginx-ingress.selectorLabels" . | nindent 4 }} ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "nginx-ingress.fullname" . }}-metrics - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} - openreplay/monitoring: nginx-ingress-metrics -spec: - selector: - {{- include "nginx-ingress.selectorLabels" . | nindent 4 }} - ports: - - name: metrics - port: 9145 - targetPort: 9145 - protocol: TCP diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/serviceaccount.yaml deleted file mode 100644 index bc0091029..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "nginx-ingress.serviceAccountName" . }} - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/tests/test-connection.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/tests/test-connection.yaml deleted file mode 100644 index 074cec518..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "nginx-ingress.fullname" . }}-test-connection" - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "nginx-ingress.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml b/scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml deleted file mode 100644 index 1f7169c0d..000000000 --- a/scripts/helmcharts/openreplay/charts/nginx-ingress/values.yaml +++ /dev/null @@ -1,109 +0,0 @@ -# Default values for nginx-ingress. -# This is a YAML-formatted file. -# Declare variables to be passed into your templates. - -replicaCount: 1 - -image: - repository: nginx - pullPolicy: IfNotPresent - # Overrides the image tag whose default is the chart appVersion. - tag: "" - -imagePullSecrets: [] -nameOverride: "" -fullnameOverride: "" - -serviceAccount: - # Specifies whether a service account should be created - create: true - # Annotations to add to the service account - annotations: {} - # The name of the service account to use. - # If not set and create is true, a name is generated using the fullname template - name: "" - -podAnnotations: {} - -podSecurityContext: {} - # fsGroup: 2000 - -securityContext: {} - # capabilities: - # drop: - # - ALL - # readOnlyRootFilesystem: true - # runAsNonRoot: true - # runAsUser: 1000 - -service: - annotations: {} - type: LoadBalancer - ports: - - port: 80 - targetPort: http - name: http - - port: 443 - targetPort: https - name: https - -ingress: - enabled: false - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local - -resources: {} - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} - -healthProbes: - livenessProbe: - failureThreshold: 3 - httpGet: - path: /healthz - port: http - scheme: HTTP - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 5 - readinessProbe: - failureThreshold: 3 - httpGet: - path: /healthz - port: http - scheme: HTTP - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 5 diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/utilities/templates/ingress.yaml index 567cac846..e17247411 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/ingress.yaml +++ b/scripts/helmcharts/openreplay/charts/utilities/templates/ingress.yaml @@ -1,61 +1,43 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.ingress.enabled }} {{- $fullName := include "utilities.fullname" . -}} -{{- $svcPort := .Values.service.port -}} -{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +{{- $peerjsSvcPort := .Values.service.ports.peerjs -}} +{{- $socketioSvcPort := .Values.service.ports.socketio -}} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} kind: Ingress metadata: name: {{ $fullName }} labels: {{- include "utilities.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$1 + nginx.ingress.kubernetes.io/upstream-hash-by: $http_x_forwarded_for + {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ingress.className }} - {{- end }} - {{- if .Values.ingress.tls }} + ingressClassName: "{{ tpl .Values.ingress.className . }}" tls: - {{- range .Values.ingress.tls }} - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} + - {{ .Values.global.domainName }} + {{- if .Values.ingress.tls.secretName}} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end}} rules: - {{- range .Values.ingress.hosts }} - - host: {{ .host | quote }} + - host: {{ .Values.global.domainName }} http: paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} + - pathType: Prefix backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} service: name: {{ $fullName }} port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} + number: {{ $peerjsSvcPort }} + path: /assist/(.*) + - pathType: Prefix + backend: + service: + name: {{ $fullName }} + port: + number: {{ $socketioSvcPort }} + path: /ws-assist/(.*) {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/utilities/values.yaml b/scripts/helmcharts/openreplay/charts/utilities/values.yaml index 8265b5aa5..3a252996c 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/values.yaml +++ b/scripts/helmcharts/openreplay/charts/utilities/values.yaml @@ -47,20 +47,13 @@ service: socketio: 9001 ingress: - enabled: false - className: "" - annotations: {} + enabled: true + className: "{{ .Values.global.ingress.controller.ingressClassResource.name }}" + annotations: # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" - hosts: - - host: chart-example.local - paths: - - path: / - pathType: ImplementationSpecific - tls: [] - # - secretName: chart-example-tls - # hosts: - # - chart-example.local + tls: + secretName: openreplay-ssl resources: {} # We usually recommend not to specify default resources and to leave this as a conscious diff --git a/scripts/helmcharts/vars.yaml b/scripts/helmcharts/vars.yaml index 24ea9344d..9c72d0d3a 100644 --- a/scripts/helmcharts/vars.yaml +++ b/scripts/helmcharts/vars.yaml @@ -41,8 +41,29 @@ minio: accessKey: "changeMeMinioAccessKey" secretKey: "changeMeMinioPassword" +ingress-nginx: &ingress-nginx + controller: + ingressClassResource: + # -- Name of the ingressClass + name: openreplay + # -- For backwards compatibility with ingress.class annotation, use ingressClass. + # Algorithm is as follows, first ingressClassName is considered, if not present, controller looks for ingress.class annotation + ingressClass: openreplay + service: + externalTrafficPolicy: "Local" + extraArgs: + default-ssl-certificate: "app/openreplay-ssl" + config: + enable-real-ip: true + forwarded-for-header: "proxy_protocol" + # If you want the ssl offload in LB layer + # Uncomment the following line + # ssl-redirect: false + # force-ssl-redirect: false + # Application specific variables global: + ingress: *ingress-nginx postgresql: *postgres kafka: *kafka redis: *redis From bd36b7c86422e264a8a1a7d8bd279644477f12e5 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 12:15:28 +0200 Subject: [PATCH 147/294] fix(helm): utilities annotation --- scripts/helmcharts/openreplay/charts/utilities/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/helmcharts/openreplay/charts/utilities/values.yaml b/scripts/helmcharts/openreplay/charts/utilities/values.yaml index 3a252996c..5696ce401 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/values.yaml +++ b/scripts/helmcharts/openreplay/charts/utilities/values.yaml @@ -49,7 +49,7 @@ service: ingress: enabled: true className: "{{ .Values.global.ingress.controller.ingressClassResource.name }}" - annotations: + annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" tls: From c48bc3e19744c56d798e7c0f2b7362aa666b35c9 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 13:26:56 +0200 Subject: [PATCH 148/294] feat(api): dashboard fixed get_domains_errors/get_domains_errors_4xx/get_domains_errors_5xx wrong neutral --- api/chalicelib/core/dashboard.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 719fabb3a..e5bc8003e 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -1,11 +1,12 @@ +import math + import schemas from chalicelib.core import metadata from chalicelib.utils import args_transformer -from chalicelib.utils import helper, dev +from chalicelib.utils import helper from chalicelib.utils import pg_client from chalicelib.utils.TimeUTC import TimeUTC from chalicelib.utils.metrics_helper import __get_step_size -import math # Written by David Aznaurov, inspired by numpy.quantile @@ -75,8 +76,6 @@ METADATA_FIELDS = {"userId": "user_id", "metadata9": "metadata_9", "metadata10": "metadata_10"} -from chalicelib.core import sessions_metas - def __get_meta_constraint(project_id, data): if len(data.get("filters", [])) == 0: @@ -127,6 +126,13 @@ SESSIONS_META_FIELDS = {"revId": "rev_id", "browser": "user_browser"} +def __get_domains_errors_neutral(rows): + neutral = {l: 0 for l in [i for k in [list(v.keys()) for v in rows] for i in k]} + if len(neutral.keys()) == 0: + neutral = {"All": 0} + return neutral + + def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): @@ -1591,7 +1597,7 @@ def get_crashes(project_id, startTimestamp=TimeUTC.now(delta_days=-1), def __get_neutral(rows, add_All_if_empty=True): neutral = {l: 0 for l in [i for k in [list(v.keys()) for v in rows] for i in k]} - if add_All_if_empty and len(neutral.keys()) == 0: + if add_All_if_empty and len(neutral.keys()) <= 1: neutral = {"All": 0} return neutral From 1f1a27797bb02482c4eca390214cd9654d134550 Mon Sep 17 00:00:00 2001 From: Rajesh Rajendran Date: Thu, 14 Apr 2022 11:56:24 +0000 Subject: [PATCH 149/294] Splitting utilities service to peers and assist (#427) * chore(helm): splitting utilities 1. peers -> handle peerjs connections 2. assist -> handle ws connections Signed-off-by: rjshrjndrn * build(utilities): rename utilities to assist Signed-off-by: rjshrjndrn * chore(build_deploy): include peers Signed-off-by: rjshrjndrn --- scripts/helm/build_deploy.sh | 2 + .../charts/{utilities => assist}/.helmignore | 0 .../charts/{utilities => assist}/Chart.yaml | 6 +- .../{utilities => assist}/templates/NOTES.txt | 8 +- .../templates/_helpers.tpl | 20 ++--- .../templates/deployment.yaml | 10 +-- .../{utilities => assist}/templates/hpa.yaml | 6 +- .../templates/ingress.yaml | 4 +- .../templates/service.yaml | 12 +-- .../templates/serviceaccount.yaml | 4 +- .../charts/{utilities => assist}/values.yaml | 6 +- .../openreplay/charts/peers/.helmignore | 23 +++++ .../openreplay/charts/peers/Chart.yaml | 24 ++++++ .../charts/peers/templates/NOTES.txt | 22 +++++ .../charts/peers/templates/_helpers.tpl | 62 ++++++++++++++ .../charts/peers/templates/deployment.yaml | 66 +++++++++++++++ .../charts/peers/templates/hpa.yaml | 28 +++++++ .../charts/peers/templates/ingress.yaml | 34 ++++++++ .../charts/peers/templates/service.yaml | 37 +++++++++ .../peers/templates/serviceaccount.yaml | 12 +++ .../templates/tests/test-connection.yaml | 15 ++++ .../openreplay/charts/peers/values.yaml | 83 +++++++++++++++++++ .../templates/tests/test-connection.yaml | 15 ---- utilities/build.sh | 8 +- 24 files changed, 450 insertions(+), 57 deletions(-) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/.helmignore (100%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/Chart.yaml (81%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/templates/NOTES.txt (77%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/templates/_helpers.tpl (75%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/templates/deployment.yaml (88%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/templates/hpa.yaml (84%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/templates/ingress.yaml (92%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/templates/service.yaml (63%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/templates/serviceaccount.yaml (66%) rename scripts/helmcharts/openreplay/charts/{utilities => assist}/values.yaml (94%) create mode 100644 scripts/helmcharts/openreplay/charts/peers/.helmignore create mode 100644 scripts/helmcharts/openreplay/charts/peers/Chart.yaml create mode 100644 scripts/helmcharts/openreplay/charts/peers/templates/NOTES.txt create mode 100644 scripts/helmcharts/openreplay/charts/peers/templates/_helpers.tpl create mode 100644 scripts/helmcharts/openreplay/charts/peers/templates/deployment.yaml create mode 100644 scripts/helmcharts/openreplay/charts/peers/templates/hpa.yaml create mode 100644 scripts/helmcharts/openreplay/charts/peers/templates/ingress.yaml create mode 100644 scripts/helmcharts/openreplay/charts/peers/templates/service.yaml create mode 100644 scripts/helmcharts/openreplay/charts/peers/templates/serviceaccount.yaml create mode 100644 scripts/helmcharts/openreplay/charts/peers/templates/tests/test-connection.yaml create mode 100644 scripts/helmcharts/openreplay/charts/peers/values.yaml delete mode 100644 scripts/helmcharts/openreplay/charts/utilities/templates/tests/test-connection.yaml diff --git a/scripts/helm/build_deploy.sh b/scripts/helm/build_deploy.sh index 86925ebe8..7a75fad8b 100644 --- a/scripts/helm/build_deploy.sh +++ b/scripts/helm/build_deploy.sh @@ -17,4 +17,6 @@ echo $DOCKER_REPO PUSH_IMAGE=1 bash build.sh $@ cd ../utilities PUSH_IMAGE=1 bash build.sh $@ + cd ../peers + PUSH_IMAGE=1 bash build.sh $@ } diff --git a/scripts/helmcharts/openreplay/charts/utilities/.helmignore b/scripts/helmcharts/openreplay/charts/assist/.helmignore similarity index 100% rename from scripts/helmcharts/openreplay/charts/utilities/.helmignore rename to scripts/helmcharts/openreplay/charts/assist/.helmignore diff --git a/scripts/helmcharts/openreplay/charts/utilities/Chart.yaml b/scripts/helmcharts/openreplay/charts/assist/Chart.yaml similarity index 81% rename from scripts/helmcharts/openreplay/charts/utilities/Chart.yaml rename to scripts/helmcharts/openreplay/charts/assist/Chart.yaml index 203871558..0bcde4fd3 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: utilities +name: assist description: A Helm chart for Kubernetes # A chart can be either an 'application' or a 'library' chart. @@ -7,8 +7,8 @@ description: A Helm chart for Kubernetes # Application charts are a collection of templates that can be packaged into versioned archives # to be deployed. # -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering +# Library charts provide useful assist or functions for the chart developer. They're included as +# a dependency of application charts to inject those assist and functions into the rendering # pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/NOTES.txt b/scripts/helmcharts/openreplay/charts/assist/templates/NOTES.txt similarity index 77% rename from scripts/helmcharts/openreplay/charts/utilities/templates/NOTES.txt rename to scripts/helmcharts/openreplay/charts/assist/templates/NOTES.txt index 323bc20c1..57c8efcf4 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/NOTES.txt +++ b/scripts/helmcharts/openreplay/charts/assist/templates/NOTES.txt @@ -6,16 +6,16 @@ {{- end }} {{- end }} {{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "utilities.fullname" . }}) + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "assist.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "utilities.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "utilities.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "assist.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "assist.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") echo http://$SERVICE_IP:{{ .Values.service.port }} {{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "utilities.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "assist.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/_helpers.tpl b/scripts/helmcharts/openreplay/charts/assist/templates/_helpers.tpl similarity index 75% rename from scripts/helmcharts/openreplay/charts/utilities/templates/_helpers.tpl rename to scripts/helmcharts/openreplay/charts/assist/templates/_helpers.tpl index 8999db4be..95cab7e2b 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/_helpers.tpl +++ b/scripts/helmcharts/openreplay/charts/assist/templates/_helpers.tpl @@ -1,7 +1,7 @@ {{/* Expand the name of the chart. */}} -{{- define "utilities.name" -}} +{{- define "assist.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} @@ -10,7 +10,7 @@ Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} -{{- define "utilities.fullname" -}} +{{- define "assist.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -26,16 +26,16 @@ If release name contains chart name it will be used as a full name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "utilities.chart" -}} +{{- define "assist.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} -{{- define "utilities.labels" -}} -helm.sh/chart: {{ include "utilities.chart" . }} -{{ include "utilities.selectorLabels" . }} +{{- define "assist.labels" -}} +helm.sh/chart: {{ include "assist.chart" . }} +{{ include "assist.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -45,17 +45,17 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{/* Selector labels */}} -{{- define "utilities.selectorLabels" -}} -app.kubernetes.io/name: {{ include "utilities.name" . }} +{{- define "assist.selectorLabels" -}} +app.kubernetes.io/name: {{ include "assist.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} -{{- define "utilities.serviceAccountName" -}} +{{- define "assist.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} -{{- default (include "utilities.fullname" .) .Values.serviceAccount.name }} +{{- default (include "assist.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml similarity index 88% rename from scripts/helmcharts/openreplay/charts/utilities/templates/deployment.yaml rename to scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml index 7a3d5b0b6..be98db206 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/deployment.yaml @@ -1,16 +1,16 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "utilities.fullname" . }} + name: {{ include "assist.fullname" . }} labels: - {{- include "utilities.labels" . | nindent 4 }} + {{- include "assist.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: - {{- include "utilities.selectorLabels" . | nindent 6 }} + {{- include "assist.selectorLabels" . | nindent 6 }} template: metadata: {{- with .Values.podAnnotations }} @@ -18,13 +18,13 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "utilities.selectorLabels" . | nindent 8 }} + {{- include "assist.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "utilities.serviceAccountName" . }} + serviceAccountName: {{ include "assist.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/hpa.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/hpa.yaml similarity index 84% rename from scripts/helmcharts/openreplay/charts/utilities/templates/hpa.yaml rename to scripts/helmcharts/openreplay/charts/assist/templates/hpa.yaml index 8944056ea..b0b394a20 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/hpa.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/hpa.yaml @@ -2,14 +2,14 @@ apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: - name: {{ include "utilities.fullname" . }} + name: {{ include "assist.fullname" . }} labels: - {{- include "utilities.labels" . | nindent 4 }} + {{- include "assist.labels" . | nindent 4 }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ include "utilities.fullname" . }} + name: {{ include "assist.fullname" . }} minReplicas: {{ .Values.autoscaling.minReplicas }} maxReplicas: {{ .Values.autoscaling.maxReplicas }} metrics: diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml similarity index 92% rename from scripts/helmcharts/openreplay/charts/utilities/templates/ingress.yaml rename to scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml index e17247411..48e568298 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/ingress.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml @@ -1,5 +1,5 @@ {{- if .Values.ingress.enabled }} -{{- $fullName := include "utilities.fullname" . -}} +{{- $fullName := include "assist.fullname" . -}} {{- $peerjsSvcPort := .Values.service.ports.peerjs -}} {{- $socketioSvcPort := .Values.service.ports.socketio -}} apiVersion: networking.k8s.io/v1 @@ -7,7 +7,7 @@ kind: Ingress metadata: name: {{ $fullName }} labels: - {{- include "utilities.labels" . | nindent 4 }} + {{- include "assist.labels" . | nindent 4 }} annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 nginx.ingress.kubernetes.io/upstream-hash-by: $http_x_forwarded_for diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/service.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/service.yaml similarity index 63% rename from scripts/helmcharts/openreplay/charts/utilities/templates/service.yaml rename to scripts/helmcharts/openreplay/charts/assist/templates/service.yaml index 8b1dc8753..699d9d149 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/service.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/service.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "utilities.fullname" . }} + name: {{ include "assist.fullname" . }} labels: - {{- include "utilities.labels" . | nindent 4 }} + {{- include "assist.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: @@ -14,15 +14,15 @@ spec: name: {{ $key }} {{- end}} selector: - {{- include "utilities.selectorLabels" . | nindent 4 }} + {{- include "assist.selectorLabels" . | nindent 4 }} --- apiVersion: v1 kind: Service metadata: - name: {{ include "utilities.fullname" . }}-headless + name: {{ include "assist.fullname" . }}-headless labels: - {{- include "utilities.labels" . | nindent 4 }} + {{- include "assist.labels" . | nindent 4 }} spec: type: ClusterIP clusterIP: None @@ -34,4 +34,4 @@ spec: name: {{ $key }} {{- end}} selector: - {{- include "utilities.selectorLabels" . | nindent 4 }} + {{- include "assist.selectorLabels" . | nindent 4 }} diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/serviceaccount.yaml similarity index 66% rename from scripts/helmcharts/openreplay/charts/utilities/templates/serviceaccount.yaml rename to scripts/helmcharts/openreplay/charts/assist/templates/serviceaccount.yaml index dd5c35012..18e86a3e8 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/serviceaccount.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/serviceaccount.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "utilities.serviceAccountName" . }} + name: {{ include "assist.serviceAccountName" . }} labels: - {{- include "utilities.labels" . | nindent 4 }} + {{- include "assist.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/scripts/helmcharts/openreplay/charts/utilities/values.yaml b/scripts/helmcharts/openreplay/charts/assist/values.yaml similarity index 94% rename from scripts/helmcharts/openreplay/charts/utilities/values.yaml rename to scripts/helmcharts/openreplay/charts/assist/values.yaml index 5696ce401..861d715f9 100644 --- a/scripts/helmcharts/openreplay/charts/utilities/values.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/values.yaml @@ -5,14 +5,14 @@ replicaCount: 1 image: - repository: rg.fr-par.scw.cloud/foss/utilities + repository: rg.fr-par.scw.cloud/foss/assist pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" imagePullSecrets: [] -nameOverride: "utililities" -fullnameOverride: "utililities" +nameOverride: "assist" +fullnameOverride: "assist" serviceAccount: # Specifies whether a service account should be created diff --git a/scripts/helmcharts/openreplay/charts/peers/.helmignore b/scripts/helmcharts/openreplay/charts/peers/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/scripts/helmcharts/openreplay/charts/peers/Chart.yaml b/scripts/helmcharts/openreplay/charts/peers/Chart.yaml new file mode 100644 index 000000000..b482f5986 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: peers +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful peers or functions for the chart developer. They're included as +# a dependency of application charts to inject those peers and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +AppVersion: "v1.5.4" diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/NOTES.txt b/scripts/helmcharts/openreplay/charts/peers/templates/NOTES.txt new file mode 100644 index 000000000..fbe8dd585 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "peers.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "peers.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "peers.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "peers.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/_helpers.tpl b/scripts/helmcharts/openreplay/charts/peers/templates/_helpers.tpl new file mode 100644 index 000000000..9dce56cdd --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "peers.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "peers.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "peers.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "peers.labels" -}} +helm.sh/chart: {{ include "peers.chart" . }} +{{ include "peers.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "peers.selectorLabels" -}} +app.kubernetes.io/name: {{ include "peers.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "peers.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "peers.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/peers/templates/deployment.yaml new file mode 100644 index 000000000..8ad4364d8 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/templates/deployment.yaml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "peers.fullname" . }} + labels: + {{- include "peers.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "peers.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "peers.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "peers.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + {{- if .Values.global.enterpriseEditionLicense }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}-ee" + {{- else }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + {{- end }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: S3_KEY + value: {{ .Values.global.s3.accessKey }} + {{- range $key, $val := .Values.env }} + - name: {{ $key }} + value: '{{ $val }}' + {{- end}} + ports: + {{- range $key, $val := .Values.service.ports }} + - name: {{ $key }} + containerPort: {{ $val }} + protocol: TCP + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/hpa.yaml b/scripts/helmcharts/openreplay/charts/peers/templates/hpa.yaml new file mode 100644 index 000000000..7191631ee --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "peers.fullname" . }} + labels: + {{- include "peers.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "peers.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/peers/templates/ingress.yaml new file mode 100644 index 000000000..0da5eca0d --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/templates/ingress.yaml @@ -0,0 +1,34 @@ +{{- if .Values.ingress.enabled }} +{{- $fullName := include "peers.fullname" . -}} +{{- $peerjsSvcPort := .Values.service.ports.peerjs -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "peers.labels" . | nindent 4 }} + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$1 + {{- with .Values.ingress.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: "{{ tpl .Values.ingress.className . }}" + tls: + - hosts: + - {{ .Values.global.domainName }} + {{- if .Values.ingress.tls.secretName}} + secretName: {{ .Values.ingress.tls.secretName }} + {{- end}} + rules: + - host: {{ .Values.global.domainName }} + http: + paths: + - pathType: Prefix + backend: + service: + name: {{ $fullName }} + port: + number: {{ $peerjsSvcPort }} + path: /assist/(.*) +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/service.yaml b/scripts/helmcharts/openreplay/charts/peers/templates/service.yaml new file mode 100644 index 000000000..6fa2a69fb --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/templates/service.yaml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "peers.fullname" . }} + labels: + {{- include "peers.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + {{- range $key, $val := .Values.service.ports }} + - port: {{ $val }} + targetPort: {{ $key }} + protocol: TCP + name: {{ $key }} + {{- end}} + selector: + {{- include "peers.selectorLabels" . | nindent 4 }} + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "peers.fullname" . }}-headless + labels: + {{- include "peers.labels" . | nindent 4 }} +spec: + type: ClusterIP + clusterIP: None + ports: + {{- range $key, $val := .Values.service.ports }} + - port: {{ $val }} + targetPort: {{ $key }} + protocol: TCP + name: {{ $key }} + {{- end}} + selector: + {{- include "peers.selectorLabels" . | nindent 4 }} diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/serviceaccount.yaml b/scripts/helmcharts/openreplay/charts/peers/templates/serviceaccount.yaml new file mode 100644 index 000000000..4b6cc04fd --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "peers.serviceAccountName" . }} + labels: + {{- include "peers.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/tests/test-connection.yaml b/scripts/helmcharts/openreplay/charts/peers/templates/tests/test-connection.yaml new file mode 100644 index 000000000..e8dd6a063 --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "peers.fullname" . }}-test-connection" + labels: + {{- include "peers.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "peers.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/scripts/helmcharts/openreplay/charts/peers/values.yaml b/scripts/helmcharts/openreplay/charts/peers/values.yaml new file mode 100644 index 000000000..ab74348fc --- /dev/null +++ b/scripts/helmcharts/openreplay/charts/peers/values.yaml @@ -0,0 +1,83 @@ +# Default values for openreplay. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: rg.fr-par.scw.cloud/foss/peers + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "peers" +fullnameOverride: "peers" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +#service: +# type: ClusterIP +# port: 9000 + +service: + type: ClusterIP + ports: + peerjs: 9000 + +ingress: + enabled: true + className: "{{ .Values.global.ingress.controller.ingressClassResource.name }}" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + tls: + secretName: openreplay-ssl + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +env: + debug: 0 + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/scripts/helmcharts/openreplay/charts/utilities/templates/tests/test-connection.yaml b/scripts/helmcharts/openreplay/charts/utilities/templates/tests/test-connection.yaml deleted file mode 100644 index 44b72f68d..000000000 --- a/scripts/helmcharts/openreplay/charts/utilities/templates/tests/test-connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: "{{ include "utilities.fullname" . }}-test-connection" - labels: - {{- include "utilities.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include "utilities.fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never diff --git a/utilities/build.sh b/utilities/build.sh index 99be144fe..f7d003ed3 100644 --- a/utilities/build.sh +++ b/utilities/build.sh @@ -20,11 +20,11 @@ function build_api(){ [[ $1 == "ee" ]] && { cp -rf ../ee/utilities/* ./ } - docker build -f ./Dockerfile -t ${DOCKER_REPO:-'local'}/utilities:${git_sha1} . + docker build -f ./Dockerfile -t ${DOCKER_REPO:-'local'}/assist:${git_sha1} . [[ $PUSH_IMAGE -eq 1 ]] && { - docker push ${DOCKER_REPO:-'local'}/utilities:${git_sha1} - docker tag ${DOCKER_REPO:-'local'}/utilities:${git_sha1} ${DOCKER_REPO:-'local'}/utilities:latest - docker push ${DOCKER_REPO:-'local'}/utilities:latest + docker push ${DOCKER_REPO:-'local'}/assist:${git_sha1} + docker tag ${DOCKER_REPO:-'local'}/assist:${git_sha1} ${DOCKER_REPO:-'local'}/assist:latest + docker push ${DOCKER_REPO:-'local'}/assist:latest } } From b7b1d2b315443e1854403c8fe8f871c4632b5d31 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 14:00:17 +0200 Subject: [PATCH 150/294] feat(api): dashboard removed unused code --- api/chalicelib/core/dashboard.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index e5bc8003e..1afa10538 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -126,13 +126,6 @@ SESSIONS_META_FIELDS = {"revId": "rev_id", "browser": "user_browser"} -def __get_domains_errors_neutral(rows): - neutral = {l: 0 for l in [i for k in [list(v.keys()) for v in rows] for i in k]} - if len(neutral.keys()) == 0: - neutral = {"All": 0} - return neutral - - def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), density=7, **args): From 53c83f99a6b7e05f09aff22fa14d6a37e7439edb Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 14:06:58 +0200 Subject: [PATCH 151/294] chore(helm): nginx change max worker connections Signed-off-by: rjshrjndrn --- scripts/helmcharts/vars.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/helmcharts/vars.yaml b/scripts/helmcharts/vars.yaml index 9c72d0d3a..1e8f268d3 100644 --- a/scripts/helmcharts/vars.yaml +++ b/scripts/helmcharts/vars.yaml @@ -56,6 +56,8 @@ ingress-nginx: &ingress-nginx config: enable-real-ip: true forwarded-for-header: "proxy_protocol" + # Ref: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#max-worker-connections + max-worker-connections: 0 # If you want the ssl offload in LB layer # Uncomment the following line # ssl-redirect: false From c174b0a63b024e8832640f8a03c59c085a6a882b Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 14:56:44 +0200 Subject: [PATCH 152/294] chore(assist): fix ports Signed-off-by: rjshrjndrn --- .../openreplay/charts/assist/templates/ingress.yaml | 10 +--------- .../helmcharts/openreplay/charts/assist/values.yaml | 1 - 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml index 48e568298..f5e2a3c03 100644 --- a/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml @@ -1,6 +1,5 @@ {{- if .Values.ingress.enabled }} {{- $fullName := include "assist.fullname" . -}} -{{- $peerjsSvcPort := .Values.service.ports.peerjs -}} {{- $socketioSvcPort := .Values.service.ports.socketio -}} apiVersion: networking.k8s.io/v1 kind: Ingress @@ -26,18 +25,11 @@ spec: - host: {{ .Values.global.domainName }} http: paths: - - pathType: Prefix - backend: - service: - name: {{ $fullName }} - port: - number: {{ $peerjsSvcPort }} - path: /assist/(.*) - pathType: Prefix backend: service: name: {{ $fullName }} port: number: {{ $socketioSvcPort }} - path: /ws-assist/(.*) + path: /assist/(.*) {{- end }} diff --git a/scripts/helmcharts/openreplay/charts/assist/values.yaml b/scripts/helmcharts/openreplay/charts/assist/values.yaml index 861d715f9..a78be6a33 100644 --- a/scripts/helmcharts/openreplay/charts/assist/values.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/values.yaml @@ -43,7 +43,6 @@ securityContext: {} service: type: ClusterIP ports: - peerjs: 9000 socketio: 9001 ingress: From 02067df36b882b141d89a7bb06eb6c76a3c81d17 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 14:58:23 +0200 Subject: [PATCH 153/294] chore(assist): remove headless port Signed-off-by: rjshrjndrn --- .../charts/assist/templates/service.yaml | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/scripts/helmcharts/openreplay/charts/assist/templates/service.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/service.yaml index 699d9d149..13b78654c 100644 --- a/scripts/helmcharts/openreplay/charts/assist/templates/service.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/service.yaml @@ -15,23 +15,3 @@ spec: {{- end}} selector: {{- include "assist.selectorLabels" . | nindent 4 }} - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "assist.fullname" . }}-headless - labels: - {{- include "assist.labels" . | nindent 4 }} -spec: - type: ClusterIP - clusterIP: None - ports: - {{- range $key, $val := .Values.service.ports }} - - port: {{ $val }} - targetPort: {{ $key }} - protocol: TCP - name: {{ $key }} - {{- end}} - selector: - {{- include "assist.selectorLabels" . | nindent 4 }} From f9df0d2b91da4e4dbaca95e1660578c312ed2629 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 14 Apr 2022 15:02:19 +0200 Subject: [PATCH 154/294] feat(ui) - dashboard - widget drilldown --- .../CallsErrors4xx/CallsErrors4xx.tsx | 9 +- .../CallsErrors5xx/CallsErrors5xx.tsx | 4 +- .../ErrorsPerDomain/ErrorsPerDomain.tsx | 1 + .../components/WidgetChart/WidgetChart.tsx | 8 +- .../components/WidgetForm/WidgetForm.tsx | 25 +++-- .../WidgetSessions/WidgetSessions.tsx | 103 ++++++++++++++---- frontend/app/components/Errors/List/List.js | 2 +- frontend/app/mstore/dashboardStore.ts | 7 +- frontend/app/mstore/types/filter.ts | 32 +++++- frontend/app/mstore/types/session.ts | 79 ++++++++++++++ frontend/app/mstore/types/widget.ts | 35 +++++- frontend/app/services/MetricService.ts | 13 +++ 12 files changed, 274 insertions(+), 44 deletions(-) create mode 100644 frontend/app/mstore/types/session.ts diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx index f1e192587..e0f721078 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { NoContent } from 'UI'; import { Styles } from '../../common'; import { - BarChart, Bar, CartesianGrid, Tooltip, + CartesianGrid, Tooltip, LineChart, Line, Legend, ResponsiveContainer, XAxis, YAxis } from 'recharts'; @@ -12,15 +12,14 @@ interface Props { metric?: any } function CallsErrors4xx(props: Props) { - const { data, metric } = props; - console.log('asd', metric.data.namesMap) + const { data, metric } = props; return ( - @@ -40,7 +39,7 @@ function CallsErrors4xx(props: Props) { { Array.isArray(metric.data.namesMap) && metric.data.namesMap.map((key, index) => ( ))} - + ); diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx index 3dec42162..cc62baf45 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors5xx/CallsErrors5xx.tsx @@ -19,7 +19,7 @@ function CallsErrors5xx(props: Props) { show={ metric.data.chart.length === 0 } > - @@ -39,7 +39,7 @@ function CallsErrors5xx(props: Props) { { Array.isArray(metric.data.namesMap) && metric.data.namesMap.map((key, index) => ( ))} - +
); diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/ErrorsPerDomain.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/ErrorsPerDomain.tsx index aaae19efa..fab8ced65 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/ErrorsPerDomain.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsPerDomain/ErrorsPerDomain.tsx @@ -16,6 +16,7 @@ function ErrorsPerDomain(props: Props) {
{metric.data.chart.map((item, i) => diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index 24cbe0cc4..c3f8d10c2 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -19,6 +19,7 @@ function WidgetChart(props: Props) { const { isWidget = false, metric } = props; const { dashboardStore } = useStore(); const period = useObserver(() => dashboardStore.period); + const drillDownFilter = useObserver(() => dashboardStore.drillDownFilter); const colors = Styles.customMetricColors; const [loading, setLoading] = useState(false) const isOverviewWidget = metric.metricType === 'predefined' && metric.viewType === 'overview'; @@ -34,6 +35,11 @@ function WidgetChart(props: Props) { const periodTimestamps = metric.metricType === 'timeseries' ? getStartAndEndTimestampsByDensity(timestamp, period.start, period.end, params.density) : period.toTimestamps(); + + drillDownFilter.merge({ + startTimestamp: periodTimestamps.startTimestamp, + endTimestamp: periodTimestamps.endTimestamp, + }); // const activeWidget = { // widget: metric, @@ -42,8 +48,6 @@ function WidgetChart(props: Props) { // timestamp: payload.timestamp, // index, // } - - // props.setActiveWidget(activeWidget); } } diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 6813a07aa..81d2ce430 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -20,7 +20,8 @@ interface Props { function WidgetForm(props: Props) { const [showDashboardSelectionModal, setShowDashboardSelectionModal] = useState(false); const { history, match: { params: { siteId, dashboardId, metricId } } } = props; - const { metricStore } = useStore(); + const { metricStore, dashboardStore } = useStore(); + const dashboards = dashboardStore.dashboards; const isSaving = useObserver(() => metricStore.isSaving); const metric: any = useObserver(() => metricStore.instance); @@ -29,6 +30,7 @@ function WidgetForm(props: Props) { const isTable = metric.metricType === 'table'; const isTimeSeries = metric.metricType === 'timeseries'; const _issueOptions = [{ text: 'All', value: 'all' }].concat(issueOptions); + const canAddToDashboard = metric.exists() && dashboards.length > 0; const write = ({ target: { value, name } }) => metricStore.merge({ [ name ]: value }); const writeOption = (e, { value, name }) => { @@ -193,7 +195,12 @@ function WidgetForm(props: Props) { Delete - @@ -201,13 +208,13 @@ function WidgetForm(props: Props) { )}
- - - setShowDashboardSelectionModal(false)} - /> + { canAddToDashboard && ( + setShowDashboardSelectionModal(false)} + /> + )} )); } diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index ccfb0fbe7..e6c9cb808 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -1,43 +1,106 @@ -import React from 'react'; -import { NoContent } from 'UI'; +import React, { useEffect, useState } from 'react'; +import { NoContent, Dropdown, Icon, Loader } from 'UI'; import cn from 'classnames'; import { useStore } from 'App/mstore'; import SessionItem from 'Shared/SessionItem'; -import { useObserver } from 'mobx-react-lite'; +import { observer, useObserver } from 'mobx-react-lite'; import { DateTime } from 'luxon'; interface Props { className?: string; } function WidgetSessions(props: Props) { const { className = '' } = props; - const { dashboardStore } = useStore(); - const filter = useObserver(() => dashboardStore.drillDownFilter); - const widget = dashboardStore.currentWidget; + const [data, setData] = useState([]); + const [seriesOptions, setSeriesOptions] = useState([ + { text: 'All', value: 'all' }, + ]); - // const range = period.toTimestamps() + const [activeSeries, setActiveSeries] = useState('all'); + + const writeOption = (e, { name, value }) => setActiveSeries(value); + useEffect(() => { + if (!data) return; + const seriesOptions = data.map(item => ({ + text: item.seriesName, + value: item.seriesId, + })); + setSeriesOptions([ + { text: 'All', value: 'all' }, + ...seriesOptions, + ]); + }, [data]); + + const filteredSessions = getListSessionsBySeries(data, activeSeries); + const { dashboardStore, metricStore } = useStore(); + const filter = useObserver(() => dashboardStore.drillDownFilter); + const widget: any = metricStore.instance; const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + useEffect(() => { + widget.fetchSessions({ ...filter, filter: widget.toJsonDrilldown()}).then(res => { + console.log('res', res) + setData(res); + }); + }, [filter.startTimestamp, filter.endTimestamp, widget.filter]); + return useObserver(() => (
-
-

Sessions

-
between {startTime} and {endTime}
+
+
+

Sessions

+
between {startTime} and {endTime}
+
+ + { widget.metricType !== 'table' && ( +
+ Series + } + /> +
+ )}
- - {widget.sessions.map((session: any) => ( - - ))} - + + + {filteredSessions.map((session: any) => ( + + ))} + +
)); } -export default WidgetSessions; \ No newline at end of file +const getListSessionsBySeries = (data, seriesId) => { + const arr: any = [] + data.forEach(element => { + if (seriesId === 'all') { + const sessionIds = arr.map(i => i.sessionId); + arr.push(...element.sessions.filter(i => !sessionIds.includes(i.sessionId))); + } else { + if (element.seriesId === seriesId) { + arr.push(...element.sessions) + } + } + }); + return arr; +} + +export default observer(WidgetSessions); \ No newline at end of file diff --git a/frontend/app/components/Errors/List/List.js b/frontend/app/components/Errors/List/List.js index 2fa91c5e5..82ecce40c 100644 --- a/frontend/app/components/Errors/List/List.js +++ b/frontend/app/components/Errors/List/List.js @@ -219,7 +219,7 @@ export default class List extends React.PureComponent { diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 73a41b379..dc8b4b999 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -5,7 +5,7 @@ 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'; -import Filter from "./types/filter"; +import Filter, { IFilter } from "./types/filter"; export interface IDashboardSotre { dashboards: IDashboard[] @@ -15,7 +15,7 @@ export interface IDashboardSotre { startTimestamp: number endTimestamp: number period: Period - drillDownFilter: Filter + drillDownFilter: IFilter siteId: any currentWidget: Widget @@ -29,6 +29,7 @@ export interface IDashboardSotre { isSaving: boolean isDeleting: boolean fetchingDashboard: boolean + sessionsLoading: boolean toggleAllSelectedWidgets: (isSelected: boolean) => void removeSelectedWidgetByCategory(category: string): void @@ -89,9 +90,11 @@ export default class DashboardStore implements IDashboardSotre { isSaving: boolean = false isDeleting: boolean = false fetchingDashboard: boolean = false + sessionsLoading: boolean = false; constructor() { makeAutoObservable(this, { + drillDownFilter: observable.ref, widgetCategories: observable.ref, resetCurrentWidget: action, addDashboard: action, diff --git a/frontend/app/mstore/types/filter.ts b/frontend/app/mstore/types/filter.ts index edd11f5fe..3c4e0aa1a 100644 --- a/frontend/app/mstore/types/filter.ts +++ b/frontend/app/mstore/types/filter.ts @@ -2,8 +2,27 @@ import { makeAutoObservable, runInAction, observable, action, reaction } from "m import { FilterKey, FilterType } from 'Types/filter/filterType' import { filtersMap } from 'Types/filter/newFilter' import FilterItem from "./filterItem" -export default class Filter { + +export interface IFilter { + filterId: string + name: string + filters: FilterItem[] + eventsOrder: string + startTimestamp: number + endTimestamp: number + + merge: (filter: any) => void + addFilter: (filter: FilterItem) => void + updateFilter: (index:number, filter: any) => void + updateKey: (key: any, value: any) => void + removeFilter: (index: number) => void + fromJson: (json: any) => void + toJson: () => any + toJsonDrilldown: () => any +} +export default class Filter implements IFilter { public static get ID_KEY():string { return "filterId" } + filterId: string = '' name: string = '' filters: FilterItem[] = [] eventsOrder: string = 'then' @@ -14,10 +33,19 @@ export default class Filter { makeAutoObservable(this, { filters: observable, eventsOrder: observable, + startTimestamp: observable, + endTimestamp: observable, addFilter: action, removeFilter: action, updateKey: action, + merge: action, + }) + } + + merge(filter: any) { + runInAction(() => { + Object.assign(this, filter) }) } @@ -36,7 +64,7 @@ export default class Filter { this.filters[index] = new FilterItem(filter) } - updateKey(key, value) { + updateKey(key: string, value) { this[key] = value } diff --git a/frontend/app/mstore/types/session.ts b/frontend/app/mstore/types/session.ts new file mode 100644 index 000000000..337e7009b --- /dev/null +++ b/frontend/app/mstore/types/session.ts @@ -0,0 +1,79 @@ +import { runInAction, makeAutoObservable, observable } from 'mobx' +import { List, Map } from 'immutable'; +import { DateTime, Duration } from 'luxon'; + +const HASH_MOD = 1610612741; +const HASH_P = 53; +function hashString(s: string): number { + let mul = 1; + let hash = 0; + for (let i = 0; i < s.length; i++) { + hash = (hash + s.charCodeAt(i) * mul) % HASH_MOD; + mul = (mul*HASH_P) % HASH_MOD; + } + return hash; +} + +export interface ISession { + sessionId: string + viewed: boolean + duration: number + metadata: any, + startedAt: number + userBrowser: string + userOs: string + userId: string + userDeviceType: string + userCountry: string + eventsCount: number + userNumericHash: number + userDisplayName: string +} + +export default class Session implements ISession { + sessionId: string = ""; + viewed: boolean = false + duration: number = 0 + metadata: any = Map() + startedAt: number = 0 + userBrowser: string = "" + userOs: string = "" + userId: string = "" + userDeviceType: string = "" + userCountry: string = "" + eventsCount: number = 0 + userNumericHash: number = 0 + userDisplayName: string = "" + + constructor() { + makeAutoObservable(this, { + sessionId: observable, + }) + } + + fromJson(session: any) { + runInAction(() => { + Object.keys(session).forEach(key => { + this[key] = session[key] + }) + + const { startTs, timestamp } = session; + const startedAt = +startTs || +timestamp; + + this.sessionId = session.sessionId + this.viewed = session.viewed + this.duration = Duration.fromMillis(session.duration < 1000 ? 1000 : session.duration); + this.metadata = Map(session.metadata) + this.startedAt = startedAt + this.userBrowser = session.userBrowser + this.userOs = session.userOs + this.userId = session.userId + this.userDeviceType = session.userDeviceType + this.eventsCount = session.eventsCount + this.userCountry = session.userCountry + this.userNumericHash = hashString(session.userId || session.userAnonymousId || session.userUuid || session.userID || session.userUUID || "") + this.userDisplayName = session.userId || session.userAnonymousId || session.userID || 'Anonymous User' + }) + return this + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index 2c74ce26b..4d20ca0f1 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -1,7 +1,9 @@ import { makeAutoObservable, runInAction, observable, action, reaction, computed } from "mobx" import FilterSeries from "./filterSeries"; import { DateTime } from 'luxon'; - +import { IFilter } from "./filter"; +import { metricService } from "App/services"; +import Session, { ISession } from "App/mstore/types/session"; export interface IWidget { metricId: any widgetId: any @@ -20,6 +22,8 @@ export interface IWidget { dashboardIds: any[] config: any + sessionsLoading: boolean + position: number data: any isLoading: boolean @@ -34,12 +38,14 @@ export interface IWidget { removeSeries(index: number): void addSeries(): void fromJson(json: any): void + toJsonDrilldown(json: any): void toJson(): any validate(): void update(data: any): void exists(): boolean toWidget(): any setData(data: any): void + fetchSessions(filter: any): Promise } export default class Widget implements IWidget { public static get ID_KEY():string { return "metricId" } @@ -61,6 +67,8 @@ export default class Widget implements IWidget { config: any = {} params: any = { density: 70 } + sessionsLoading: boolean = false + position: number = 0 data: any = { chart: [], @@ -74,7 +82,9 @@ export default class Widget implements IWidget { constructor() { makeAutoObservable(this, { + sessionsLoading: observable, data: observable.ref, + metricId: observable, widgetId: observable, name: observable, metricType: observable, @@ -146,6 +156,12 @@ export default class Widget implements IWidget { } } + toJsonDrilldown() { + return { + series: this.series.map((series: any) => series.toJson()), + } + } + toJson() { return { metricId: this.metricId, @@ -179,4 +195,21 @@ export default class Widget implements IWidget { Object.assign(this.data, data) }) } + + fetchSessions(filter: any): Promise { + this.sessionsLoading = true + return new Promise((resolve, reject) => { + console.log('fetching sessions', filter) + metricService.fetchSessions(this.metricId, filter).then(response => { + resolve(response.map(cat => { + return { + ...cat, + sessions: cat.sessions.map(s => new Session().fromJson(s)) + } + })) + }).finally(() => { + this.sessionsLoading = false + }) + }) + } } \ No newline at end of file diff --git a/frontend/app/services/MetricService.ts b/frontend/app/services/MetricService.ts index a62520d52..0007bc8d1 100644 --- a/frontend/app/services/MetricService.ts +++ b/frontend/app/services/MetricService.ts @@ -1,5 +1,6 @@ import Widget, { IWidget } from "App/mstore/types/widget"; import APIClient from 'App/api_client'; +import { IFilter } from "App/mstore/types/filter"; export interface IMetricService { initClient(client?: APIClient): void; @@ -11,6 +12,7 @@ export interface IMetricService { getTemplates(): Promise; getMetricChartData(metric: IWidget, data: any, isWidget: boolean): Promise; + fetchSessions(metricId: string, filter: any): Promise } export default class MetricService implements IMetricService { @@ -88,4 +90,15 @@ export default class MetricService implements IMetricService { .then(response => response.json()) .then(response => response.data || {}); } + + /** + * Fetch sessions from the server. + * @param filter + * @returns + */ + fetchSessions(metricId: string, filter: any): Promise { + return this.client.post(`/metrics/${metricId}/sessions`, filter) + .then(response => response.json()) + .then(response => response.data || []); + } } \ No newline at end of file From 0b16448e419339bedb3473681ab989213049ff1e Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 15:02:55 +0200 Subject: [PATCH 155/294] chore(peers): remove headless svc Signed-off-by: rjshrjndrn --- .../charts/peers/templates/service.yaml | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/scripts/helmcharts/openreplay/charts/peers/templates/service.yaml b/scripts/helmcharts/openreplay/charts/peers/templates/service.yaml index 6fa2a69fb..3a069c2c2 100644 --- a/scripts/helmcharts/openreplay/charts/peers/templates/service.yaml +++ b/scripts/helmcharts/openreplay/charts/peers/templates/service.yaml @@ -15,23 +15,3 @@ spec: {{- end}} selector: {{- include "peers.selectorLabels" . | nindent 4 }} - ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "peers.fullname" . }}-headless - labels: - {{- include "peers.labels" . | nindent 4 }} -spec: - type: ClusterIP - clusterIP: None - ports: - {{- range $key, $val := .Values.service.ports }} - - port: {{ $val }} - targetPort: {{ $key }} - protocol: TCP - name: {{ $key }} - {{- end}} - selector: - {{- include "peers.selectorLabels" . | nindent 4 }} From 61247e5d416d9e5cfd5d456df4d3a1765e773e1b Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 15:29:44 +0200 Subject: [PATCH 156/294] chore(kubernetes): upgrade kube Signed-off-by: rjshrjndrn --- scripts/helmcharts/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/helmcharts/init.sh b/scripts/helmcharts/init.sh index c772629fc..078723007 100644 --- a/scripts/helmcharts/init.sh +++ b/scripts/helmcharts/init.sh @@ -19,7 +19,7 @@ version="v1.5.4" usr=`whoami` # Installing k3s -curl -sL https://get.k3s.io | sudo K3S_KUBECONFIG_MODE="644" INSTALL_K3S_VERSION='v1.19.5+k3s2' INSTALL_K3S_EXEC="--no-deploy=traefik" sh - +curl -sL https://get.k3s.io | sudo K3S_KUBECONFIG_MODE="644" INSTALL_K3S_VERSION='v1.22.8+k3s1' INSTALL_K3S_EXEC="--no-deploy=traefik" sh - mkdir ~/.kube sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config chmod 0644 ~/.kube/config From 44d5f8bc104e8ea0a8a5ceda24fafa6ff3b5a96e Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 15:39:04 +0200 Subject: [PATCH 157/294] chore(helm): adding clusterIssuer for ssl Signed-off-by: rjshrjndrn --- scripts/helmcharts/clusterIssuer.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 scripts/helmcharts/clusterIssuer.yaml diff --git a/scripts/helmcharts/clusterIssuer.yaml b/scripts/helmcharts/clusterIssuer.yaml new file mode 100644 index 000000000..fa18dfb3e --- /dev/null +++ b/scripts/helmcharts/clusterIssuer.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + # The ACME server URL + server: https://acme-v02.api.letsencrypt.org/directory + # Email address used for ACME registration + email: + # Name of a secret used to store the ACME account private key + privateKeySecretRef: + name: letsencrypt-prod + # Enable the HTTP-01 challenge provider + solvers: + - http01: + ingress: + class: openreplay From ac9fdf1091735c6a6ec118b7b2396ef624d2bcb2 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 15:43:06 +0200 Subject: [PATCH 158/294] fix(certmanager): email update Signed-off-by: rjshrjndrn --- scripts/helmcharts/certmanager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/helmcharts/certmanager.sh b/scripts/helmcharts/certmanager.sh index 23f138084..1b8a57422 100644 --- a/scripts/helmcharts/certmanager.sh +++ b/scripts/helmcharts/certmanager.sh @@ -25,7 +25,7 @@ else fatal "Email address $EMAIL_ADDRESS is invalid." fi -sed -i "s/email: \"\"/email: \"${EMAIL_ADDRESS}\"/g" clusterIssuer.yaml +sed -i "s/email: .*/email: \"${EMAIL_ADDRESS}\"/g" clusterIssuer.yaml info "Installing cert-manager for auto letsencrypt certificate" kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.crds.yaml helm repo add jetstack https://charts.jetstack.io From 6f5bda117c4fd2fe6767c4d19bc6146570c18f83 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 15:48:37 +0200 Subject: [PATCH 159/294] fix(ingress): assist path Signed-off-by: rjshrjndrn --- .../helmcharts/openreplay/charts/assist/templates/ingress.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml b/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml index f5e2a3c03..f791024bb 100644 --- a/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/templates/ingress.yaml @@ -31,5 +31,5 @@ spec: name: {{ $fullName }} port: number: {{ $socketioSvcPort }} - path: /assist/(.*) + path: /ws-assist/(.*) {{- end }} From e103582af983c06c755d6dd682ed87e75fbce60f Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 16:00:44 +0200 Subject: [PATCH 160/294] feat(api): changed assist configuration --- api/.env.default | 3 +-- api/chalicelib/core/assist.py | 47 ++--------------------------------- ee/api/.env.default | 3 +-- 3 files changed, 4 insertions(+), 49 deletions(-) diff --git a/api/.env.default b/api/.env.default index 6caeb96d8..8b79b49cd 100644 --- a/api/.env.default +++ b/api/.env.default @@ -28,8 +28,7 @@ jwt_algorithm=HS512 jwt_exp_delta_seconds=2592000 jwt_issuer=openreplay-default-foss jwt_secret="SET A RANDOM STRING HERE" -peersList=http://utilities-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-list -peers=http://utilities-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-live +assist=http://assist-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-list pg_dbname=postgres pg_host=postgresql.db.svc.cluster.local pg_password=asayerPostgres diff --git a/api/chalicelib/core/assist.py b/api/chalicelib/core/assist.py index c6fb35713..122c64dee 100644 --- a/api/chalicelib/core/assist.py +++ b/api/chalicelib/core/assist.py @@ -21,56 +21,13 @@ SESSION_PROJECTION_COLS = """s.project_id, """ -def get_live_sessions(project_id, filters=None): - project_key = projects.get_project_key(project_id) - connected_peers = requests.get(config("peers") % config("S3_KEY") + f"/{project_key}") - if connected_peers.status_code != 200: - print("!! issue with the peer-server") - print(connected_peers.text) - return [] - connected_peers = connected_peers.json().get("data", []) - - if len(connected_peers) == 0: - return [] - connected_peers = tuple(connected_peers) - extra_constraints = ["project_id = %(project_id)s", "session_id IN %(connected_peers)s"] - extra_params = {} - if filters is not None: - for i, f in enumerate(filters): - if not isinstance(f.get("value"), list): - f["value"] = [f.get("value")] - if len(f["value"]) == 0 or f["value"][0] is None: - continue - filter_type = f["type"].upper() - f["value"] = sessions.__get_sql_value_multiple(f["value"]) - if filter_type == schemas.FilterType.user_id: - op = sessions.__get_sql_operator(f["operator"]) - extra_constraints.append(f"user_id {op} %(value_{i})s") - extra_params[f"value_{i}"] = helper.string_to_sql_like_with_op(f["value"][0], op) - - with pg_client.PostgresClient() as cur: - query = cur.mogrify(f"""\ - SELECT {SESSION_PROJECTION_COLS}, %(project_key)s||'-'|| session_id AS peer_id - FROM public.sessions AS s - WHERE {" AND ".join(extra_constraints)} - ORDER BY start_ts DESC - LIMIT 500;""", - {"project_id": project_id, - "connected_peers": connected_peers, - "project_key": project_key, - **extra_params}) - cur.execute(query) - results = cur.fetchall() - return helper.list_to_camel_case(results) - - def get_live_sessions_ws(project_id, user_id=None): project_key = projects.get_project_key(project_id) params = {} if user_id and len(user_id) > 0: params["userId"] = user_id try: - connected_peers = requests.get(config("peers") % config("S3_KEY") + f"/{project_key}", params) + connected_peers = requests.get(config("assist") % config("S3_KEY") + f"/{project_key}", params) if connected_peers.status_code != 200: print("!! issue with the peer-server") print(connected_peers.text) @@ -105,7 +62,7 @@ def is_live(project_id, session_id, project_key=None): if project_key is None: project_key = projects.get_project_key(project_id) try: - connected_peers = requests.get(config("peersList") % config("S3_KEY") + f"/{project_key}") + connected_peers = requests.get(config("assist") % config("S3_KEY") + f"/{project_key}") if connected_peers.status_code != 200: print("!! issue with the peer-server") print(connected_peers.text) diff --git a/ee/api/.env.default b/ee/api/.env.default index b4b1ad291..c7befe0ef 100644 --- a/ee/api/.env.default +++ b/ee/api/.env.default @@ -37,8 +37,7 @@ jwt_algorithm=HS512 jwt_exp_delta_seconds=2592000 jwt_issuer=openreplay-default-ee jwt_secret="SET A RANDOM STRING HERE" -peersList=http://utilities-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-list -peers=http://utilities-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-live +assist=http://assist-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-list pg_dbname=postgres pg_host=postgresql.db.svc.cluster.local pg_password=asayerPostgres From 1148da80112e588886985d6ed72d2829dcd9ca5f Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 16:16:05 +0200 Subject: [PATCH 161/294] feat(api): custom metrics support series override --- api/chalicelib/core/custom_metrics.py | 2 ++ api/schemas.py | 1 + 2 files changed, 3 insertions(+) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 15c2ffc49..7c6311b75 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -57,6 +57,8 @@ def merged_live(project_id, data: schemas.TryCustomMetricsPayloadSchema): def __merge_metric_with_data(metric, data: Union[schemas.CustomMetricChartPayloadSchema, schemas.CustomMetricSessionsPayloadSchema]) \ -> Union[schemas.CreateCustomMetricsSchema, None]: + if data.series is not None and len(data.series) > 0: + metric["series"] = data.series 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: diff --git a/api/schemas.py b/api/schemas.py index b01553cc8..07455eb72 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -806,6 +806,7 @@ class TimeseriesMetricOfType(str, Enum): class CustomMetricSessionsPayloadSchema(FlatSessionsSearch): startTimestamp: int = Field(TimeUTC.now(-7)) endTimestamp: int = Field(TimeUTC.now()) + series: Optional[List[CustomMetricCreateSeriesSchema]] = Field(default=None) class Config: alias_generator = attribute_to_camel_case From 87049b4633e9df117fd1b2491b739e1c950e87ef Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 16:26:17 +0200 Subject: [PATCH 162/294] chore(certmanager): check domain name has ip, then proceed Signed-off-by: rjshrjndrn --- scripts/helmcharts/certmanager.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/helmcharts/certmanager.sh b/scripts/helmcharts/certmanager.sh index 1b8a57422..64ba77b14 100644 --- a/scripts/helmcharts/certmanager.sh +++ b/scripts/helmcharts/certmanager.sh @@ -13,6 +13,11 @@ fatal() exit 1 } +read -p "enter openreplay domain name: " domain +nslookup domain > /dev/null || { + fatal "Domain name doesn't have ip associated with it. Please check your DNS record." +} + # Reading email address for ssl certificate [[ -z $EMAIL_ADDRESS ]] && { read -p "Enter your email address for letsencrypt certificate: " EMAIL_ADDRESS From 3cae5e557557b3a4f8c1488a5246feee612181d6 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 16:40:06 +0200 Subject: [PATCH 163/294] feat(api): dashboard fixed date-time format for dashboards feat(api): dashboard fixed date-time format for widgets feat(api): dashboard fixed date-time format for series feat(api): dashboard support empty series widgets --- api/chalicelib/core/dashboards2.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index a66324532..38fd2bcdf 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -85,7 +85,7 @@ def get_dashboard(project_id, user_id, dashboard_id): 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 + LEFT JOIN LATERAL (SELECT COALESCE(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 @@ -102,9 +102,12 @@ def get_dashboard(project_id, user_id, dashboard_id): cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() if row is not None: + row["created_at"] = TimeUTC.datetime_to_timestamp(row["created_at"]) 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"]) + w["created_at"] = TimeUTC.datetime_to_timestamp(w["created_at"]) + w["edited_at"] = TimeUTC.datetime_to_timestamp(w["edited_at"]) + for s in w["series"]: + s["created_at"] = TimeUTC.datetime_to_timestamp(s["created_at"]) return helper.dict_to_camel_case(row) From af39480e09642d1bf23f729a4ee08e888b4ecdda Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 14 Apr 2022 17:00:02 +0200 Subject: [PATCH 164/294] feat(ui) - dashboard - ui review and fixes --- .../CustomMetricPercentage.tsx | 2 +- .../SlowestDomains/SlowestDomains.tsx | 1 + .../DashboardMetricSelection.tsx | 22 ++++++--- .../DashboardModal/DashboardModal.tsx | 8 ++-- .../DashboardSelectionModal.tsx | 2 +- .../DashboardSideMenu/DashboardSideMenu.tsx | 2 + .../DashboardView/DashboardView.tsx | 9 ++-- .../components/WidgetView/WidgetView.tsx | 2 +- .../WidgetWrapper/TemplateOverlay.tsx | 23 +++++++++ .../WidgetWrapper/WidgetWrapper.tsx | 29 +++++++----- .../app/components/Modal/ModalOverlay.tsx | 2 +- .../app/components/ui/ItemMenu/ItemMenu.js | 47 ++++++++++++------- frontend/app/mstore/dashboardStore.ts | 8 ++++ 13 files changed, 109 insertions(+), 48 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx index 7e3681f45..ffce73783 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricPercentage/CustomMetricPercentage.tsx @@ -12,7 +12,7 @@ function CustomMetriPercentage(props: Props) { return (
{numberWithCommas(data.count)}
-
{`${parseInt(data.previousCount)} ( ${parseInt(data.countProgress).toFixed(1)}% )`}
+
{`${parseInt(data.previousCount || 0)} ( ${parseInt(data.countProgress || 0).toFixed(1)}% )`}
from previous period.
) diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx index 9e2563752..2d74e2b39 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestDomains/SlowestDomains.tsx @@ -15,6 +15,7 @@ function SlowestDomains(props: Props) {
{metric.data.chart.map((item, i) => diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index 371815135..a999e0358 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -17,8 +17,8 @@ function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds,
{category.description}
{selectedCategoryWidgetsCount > 0 && (
- unSelectCategory(category)} /> - {`Selected ${selectedCategoryWidgetsCount} of ${category.widgets.length}`} + {/* unSelectCategory(category)} /> */} + {`Selected ${selectedCategoryWidgetsCount} of ${category.widgets.length}`}
)}
@@ -29,6 +29,7 @@ function DashboardMetricSelection(props) { const { dashboardStore } = useStore(); let widgetCategories: any[] = useObserver(() => dashboardStore.widgetCategories); const [activeCategory, setActiveCategory] = React.useState(); + const [selectAllCheck, setSelectAllCheck] = React.useState(false); const selectedWidgetIds = useObserver(() => dashboardStore.selectedWidgets.map((widget: any) => widget.metricId)); useEffect(() => { @@ -39,10 +40,17 @@ function DashboardMetricSelection(props) { const handleWidgetCategoryClick = (category: any) => { setActiveCategory(category); + setSelectAllCheck(false); }; const toggleAllWidgets = ({ target: { checked }}) => { - dashboardStore.toggleAllSelectedWidgets(checked); + // dashboardStore.toggleAllSelectedWidgets(checked); + setSelectAllCheck(checked); + if (checked) { + dashboardStore.selectWidgetsByCategory(activeCategory.name); + } else { + dashboardStore.removeSelectedWidgetByCategory(activeCategory); + } } return useObserver(() => ( @@ -62,10 +70,10 @@ function DashboardMetricSelection(props) {
Showing past 7 days data for visual clue -
- - Select All -
+
)} diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 4814ada0d..35277dc74 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -16,6 +16,7 @@ interface Props { function DashboardModal(props) { const { history, siteId, dashboardId } = props; const { dashboardStore } = useStore(); + const selectedWidgetsCount = useObserver(() => dashboardStore.selectedWidgets.length); const { hideModal } = useModal(); const dashboard = useObserver(() => dashboardStore.dashboardInstance); const loading = useObserver(() => dashboardStore.isSaving); @@ -33,7 +34,7 @@ function DashboardModal(props) { return useObserver(() => (
@@ -53,15 +54,16 @@ function DashboardModal(props) { )} -
+
+ {selectedWidgetsCount} Widgets
)); diff --git a/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx b/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx index f7a2c46ac..830730d7b 100644 --- a/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSelectionModal/DashboardSelectionModal.tsx @@ -29,7 +29,7 @@ function DashboardSelectionModal(props: Props) { return useObserver(() => ( -
{ 'Add to selected Dashboard' }
+
{ 'Add to selected dashboard' }
dashboardStore.selectedDashboard?.dashboardId); const dashboardsPicked = useObserver(() => dashboardStore.dashboards.slice(0, SHOW_COUNT)); const remainingDashboardsCount = dashboardStore.dashboards.length - SHOW_COUNT; + const isMetric = history.location.pathname.includes('metrics'); const redirect = (path) => { history.push(path); @@ -82,6 +83,7 @@ function DashboardSideMenu(props: Props) {
Create Dashboard } > -
+
setShowEditModal(false)} @@ -98,15 +98,16 @@ function DashboardView(props: Props) {
- More + {/* Options */} setExpanded(!expanded)} className="flex items-center cursor-pointer select-none" > - {expanded ? 'Collapse' : 'Expand'} + {expanded ? 'Close' : 'Edit'}
diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx new file mode 100644 index 000000000..3b76f36de --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Tooltip } from 'react-tippy'; + +function TemplateOverlay() { + return ( +
+ +
+ +
+ ); +} + +export default TemplateOverlay; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 6bb9ca875..2aadc8733 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -9,6 +9,7 @@ import { useStore } from 'App/mstore'; import LazyLoad from 'react-lazyload'; import { withRouter } from 'react-router-dom'; import { withSiteId, dashboardMetricDetails } from 'App/routes'; +import TemplateOverlay from './TemplateOverlay'; interface Props { className?: string; @@ -27,7 +28,7 @@ interface Props { function WidgetWrapper(props: Props) { const { dashboardStore } = useStore(); const { isWidget = false, active = false, index = 0, moveListItem = null, isPreview = false, isTemplate = false, dashboardId, siteId } = props; - const widget = useObserver(() => props.widget); + const widget: any = useObserver(() => props.widget); const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -40,7 +41,6 @@ function WidgetWrapper(props: Props) { const [{ isOver, canDrop }, dropRef] = useDrop({ accept: 'item', - drop: (item: any) => { if (item.index === index) return; moveListItem(item.index, index); @@ -52,13 +52,14 @@ function WidgetWrapper(props: Props) { }) const onDelete = async () => { - if (await confirm({ - header: 'Confirm', - confirmButton: 'Yes, delete', - confirmation: `Are you sure you want to permanently delete the widget from this dashboard?` - })) { - dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); - } + dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); + // if (await confirm({ + // header: 'Confirm', + // confirmButton: 'Yes, delete', + // confirmation: `Are you sure you want to permanently delete the widget from this dashboard?` + // })) { + // dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); + // } } const onChartClick = () => { @@ -72,9 +73,8 @@ function WidgetWrapper(props: Props) { return useObserver(() => (
{}} > + {isTemplate && }

{widget.name}

{isWidget && ( @@ -92,9 +93,11 @@ function WidgetWrapper(props: Props) { items={[ { text: 'Edit', onClick: onChartClick, + disabled: widget.metricType === 'predefined', + disabledMessage: 'Cannot edit system generated metrics' }, { - text: 'Hide from view', + text: 'Remove from view', onClick: onDelete }, ]} diff --git a/frontend/app/components/Modal/ModalOverlay.tsx b/frontend/app/components/Modal/ModalOverlay.tsx index 85b314eec..4dad0ec61 100644 --- a/frontend/app/components/Modal/ModalOverlay.tsx +++ b/frontend/app/components/Modal/ModalOverlay.tsx @@ -6,7 +6,7 @@ function ModalOverlay({ children }) { let modal = useModal(); return ( -
+
modal.hideModal()} className={stl.overlay} diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.js b/frontend/app/components/ui/ItemMenu/ItemMenu.js index 2663a7b2a..841d9e4c9 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.js +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.js @@ -2,7 +2,7 @@ import { Icon } from 'UI'; import styles from './itemMenu.css'; import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv'; import cn from 'classnames'; - +import { Tooltip } from 'react-tippy'; export default class ItemMenu extends React.PureComponent { state = { displayed: false, @@ -20,7 +20,7 @@ export default class ItemMenu extends React.PureComponent { closeMenu = () => this.setState({ displayed: false }) render() { - const { items } = this.props; + const { items, label = "" } = this.props; const { displayed } = this.state; return ( @@ -28,33 +28,46 @@ export default class ItemMenu extends React.PureComponent { -
{ this.menuBtnRef = ref; } } - 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" - > - +
+ {label && {label}} +
{ this.menuBtnRef = ref; } } + className={cn("w-10 h-10 rounded-full flex items-center justify-center hover:bg-gray-light", { 'bg-gray-light' : displayed })} + role="button" + > + +
- { items.filter(({ hidden }) => !hidden).map(({ onClick, text, icon }) => ( + { items.filter(({ hidden }) => !hidden).map(({ onClick, text, icon, disabled = false, disabledMessage = '' }) => (
{} } role="menuitem" tabIndex="-1" > - { icon && ( -
- + +
+ { icon && ( +
+ +
+ )} +
{ text }
- )} -
{ text }
+
))}
diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index dc8b4b999..fb33ac89e 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -31,6 +31,7 @@ export interface IDashboardSotre { fetchingDashboard: boolean sessionsLoading: boolean + selectWidgetsByCategory: (category: string) => void toggleAllSelectedWidgets: (isSelected: boolean) => void removeSelectedWidgetByCategory(category: string): void toggleWidgetSelection(widget: IWidget): void @@ -111,6 +112,7 @@ export default class DashboardStore implements IDashboardSotre { editWidget: action, updateKey: action, + selectWidgetsByCategory: action, toggleAllSelectedWidgets: action, removeSelectedWidgetByCategory: action, toggleWidgetSelection: action, @@ -138,6 +140,12 @@ export default class DashboardStore implements IDashboardSotre { } } + selectWidgetsByCategory(category: string) { + const selectedWidgetIds = this.selectedWidgets.map((widget: any) => widget.metricId); + const widgets = this.widgetCategories.find(cat => cat.name === category)?.widgets.filter(widget => !selectedWidgetIds.includes(widget.metricId)) + this.selectedWidgets = this.selectedWidgets.concat(widgets) || [] + } + removeSelectedWidgetByCategory = (category: any) => { const categoryWidgetIds = category.widgets.map(w => w.metricId) this.selectedWidgets = this.selectedWidgets.filter((widget: any) => !categoryWidgetIds.includes(widget.metricId)); From ffb82f38fcd1fb19816b4ee357e108085593fc34 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 17:14:09 +0200 Subject: [PATCH 165/294] feat(api): fixed assist configuration --- api/.env.default | 3 ++- api/chalicelib/core/assist.py | 2 +- ee/api/.env.default | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/api/.env.default b/api/.env.default index 8b79b49cd..7dd248bec 100644 --- a/api/.env.default +++ b/api/.env.default @@ -28,7 +28,8 @@ jwt_algorithm=HS512 jwt_exp_delta_seconds=2592000 jwt_issuer=openreplay-default-foss jwt_secret="SET A RANDOM STRING HERE" -assist=http://assist-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-list +assist=http://assist-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-live +assistList=http://assist-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-list pg_dbname=postgres pg_host=postgresql.db.svc.cluster.local pg_password=asayerPostgres diff --git a/api/chalicelib/core/assist.py b/api/chalicelib/core/assist.py index 122c64dee..97bff84fd 100644 --- a/api/chalicelib/core/assist.py +++ b/api/chalicelib/core/assist.py @@ -62,7 +62,7 @@ def is_live(project_id, session_id, project_key=None): if project_key is None: project_key = projects.get_project_key(project_id) try: - connected_peers = requests.get(config("assist") % config("S3_KEY") + f"/{project_key}") + connected_peers = requests.get(config("assistList") % config("S3_KEY") + f"/{project_key}") if connected_peers.status_code != 200: print("!! issue with the peer-server") print(connected_peers.text) diff --git a/ee/api/.env.default b/ee/api/.env.default index c7befe0ef..094579f1b 100644 --- a/ee/api/.env.default +++ b/ee/api/.env.default @@ -37,7 +37,8 @@ jwt_algorithm=HS512 jwt_exp_delta_seconds=2592000 jwt_issuer=openreplay-default-ee jwt_secret="SET A RANDOM STRING HERE" -assist=http://assist-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-list +assist=http://assist-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-live +assistList=http://assist-openreplay.app.svc.cluster.local:9001/assist/%s/sockets-list pg_dbname=postgres pg_host=postgresql.db.svc.cluster.local pg_password=asayerPostgres From 5cc49b9e9b430247409e49f896725833819622d7 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 17:31:49 +0200 Subject: [PATCH 166/294] chore(helm): overriding svc name template for assist/peers Signed-off-by: rjshrjndrn --- scripts/helmcharts/openreplay/values.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/helmcharts/openreplay/values.yaml b/scripts/helmcharts/openreplay/values.yaml index 7fcd4121e..f702c1a49 100644 --- a/scripts/helmcharts/openreplay/values.yaml +++ b/scripts/helmcharts/openreplay/values.yaml @@ -110,5 +110,7 @@ sink: fullnameOverride: sink-openreplay storage: fullnameOverride: storage-openreplay -utilities: - fullnameOverride: utilities-openreplay +assist: + fullnameOverride: assist-openreplay +peers: + fullnameOverride: peers-openreplay From 60cb0ea9b3256e1dbfe4b7f62874fdf14e620c6f Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 18:17:08 +0200 Subject: [PATCH 167/294] feat(assist): fixed geoip race-condition --- ee/utilities/servers/websocket-cluster.js | 4 ++-- ee/utilities/servers/websocket.js | 4 ++-- utilities/servers/websocket.js | 4 ++-- utilities/utils/geoIP.js | 6 +++++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ee/utilities/servers/websocket-cluster.js b/ee/utilities/servers/websocket-cluster.js index f21755f9b..59ab97927 100644 --- a/ee/utilities/servers/websocket-cluster.js +++ b/ee/utilities/servers/websocket-cluster.js @@ -268,9 +268,9 @@ function extractSessionInfo(socket) { socket.handshake.query.sessionInfo.userDevice = ua.device.model || null; socket.handshake.query.sessionInfo.userDeviceType = ua.device.type || 'desktop'; socket.handshake.query.sessionInfo.userCountry = null; - if (geoip !== null) { + if (geoip() !== null) { debug && console.log(`looking for location of ${socket.handshake.headers['x-forwarded-for'] || socket.handshake.address}`); - let country = geoip.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); + let country = geoip().country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; } } diff --git a/ee/utilities/servers/websocket.js b/ee/utilities/servers/websocket.js index a5126a78f..a20f23078 100644 --- a/ee/utilities/servers/websocket.js +++ b/ee/utilities/servers/websocket.js @@ -247,9 +247,9 @@ function extractSessionInfo(socket) { socket.handshake.query.sessionInfo.userDevice = ua.device.model || null; socket.handshake.query.sessionInfo.userDeviceType = ua.device.type || 'desktop'; socket.handshake.query.sessionInfo.userCountry = null; - if (geoip !== null) { + if (geoip() !== null) { debug && console.log(`looking for location of ${socket.handshake.headers['x-forwarded-for'] || socket.handshake.address}`); - let country = geoip.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); + let country = geoip().country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; } } diff --git a/utilities/servers/websocket.js b/utilities/servers/websocket.js index 2c4d22342..2d5b6fc4e 100644 --- a/utilities/servers/websocket.js +++ b/utilities/servers/websocket.js @@ -218,9 +218,9 @@ function extractSessionInfo(socket) { socket.handshake.query.sessionInfo.userDevice = ua.device.model || null; socket.handshake.query.sessionInfo.userDeviceType = ua.device.type || 'desktop'; socket.handshake.query.sessionInfo.userCountry = null; - if (geoip !== null) { + if (geoip() !== null) { debug && console.log(`looking for location of ${socket.handshake.headers['x-forwarded-for'] || socket.handshake.address}`); - let country = geoip.country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); + let country = geoip().country(socket.handshake.headers['x-forwarded-for'] || socket.handshake.address); socket.handshake.query.sessionInfo.userCountry = country.country.isoCode; } } diff --git a/utilities/utils/geoIP.js b/utilities/utils/geoIP.js index 5bfcfd0e8..ae3955101 100644 --- a/utilities/utils/geoIP.js +++ b/utilities/utils/geoIP.js @@ -13,4 +13,8 @@ if (process.env.MAXMINDDB_FILE !== undefined) { console.error("!!! please provide a valid value for MAXMINDDB_FILE env var."); } -module.exports = {geoip} \ No newline at end of file +module.exports = { + geoip: () => { + return geoip; + } +} \ No newline at end of file From ab7cdb411d2aa773b3feed2890e99580b6a2d663 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 18:25:58 +0200 Subject: [PATCH 168/294] feat(api): sessions search requests by base_path instead of URL --- api/chalicelib/core/events.py | 2 +- api/chalicelib/core/sessions.py | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/api/chalicelib/core/events.py b/api/chalicelib/core/events.py index 933e3f800..3fbb4f70b 100644 --- a/api/chalicelib/core/events.py +++ b/api/chalicelib/core/events.py @@ -341,7 +341,7 @@ class event_type: INPUT = Event(ui_type=schemas.EventType.input, table="events.inputs", column="label") LOCATION = Event(ui_type=schemas.EventType.location, table="events.pages", column="base_path") CUSTOM = Event(ui_type=schemas.EventType.custom, table="events_common.customs", column="name") - REQUEST = Event(ui_type=schemas.EventType.request, table="events_common.requests", column="url") + REQUEST = Event(ui_type=schemas.EventType.request, table="events_common.requests", column="base_path") GRAPHQL = Event(ui_type=schemas.EventType.graphql, table="events.graphql", column="name") STATEACTION = Event(ui_type=schemas.EventType.state_action, table="events.state_actions", column="name") ERROR = Event(ui_type=schemas.EventType.error, table="events.errors", diff --git a/api/chalicelib/core/sessions.py b/api/chalicelib/core/sessions.py index c0fdf57bd..380ed2dd2 100644 --- a/api/chalicelib/core/sessions.py +++ b/api/chalicelib/core/sessions.py @@ -3,7 +3,7 @@ from typing import List import schemas from chalicelib.core import events, metadata, events_ios, \ sessions_mobs, issues, projects, errors, resources, assist, performance_event -from chalicelib.utils import pg_client, helper, dev, metrics_helper +from chalicelib.utils import pg_client, helper, metrics_helper SESSION_PROJECTION_COLS = """s.project_id, s.session_id::text AS session_id, @@ -168,7 +168,6 @@ def _isUndefined_operator(op: schemas.SearchEventOperator): return op in [schemas.SearchEventOperator._is_undefined] -@dev.timed def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, errors_only=False, error_status=schemas.ErrorStatus.all, count_only=False, issue=None): full_args, query_part = search_query_parts(data=data, error_status=error_status, errors_only=errors_only, @@ -659,11 +658,6 @@ def search_query_parts(data, error_status, errors_only, favorite_only, issue, pr **_multiple_values(event.value, value_key=e_k), **_multiple_values(event.source, value_key=s_k)} - # if event_type not in list(events.SUPPORTED_TYPES.keys()) \ - # or event.value in [None, "", "*"] \ - # and (event_type != events.event_type.ERROR.ui_type \ - # or event_type != events.event_type.ERROR_IOS.ui_type): - # continue if event_type == events.event_type.CLICK.ui_type: event_from = event_from % f"{events.event_type.CLICK.table} AS main " if not is_any: From 2c3c0c30c085ab08720a65cbe56733d257ae1ec6 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 14 Apr 2022 18:29:33 +0200 Subject: [PATCH 169/294] feat(ui) - dashboard - ui review and fixes --- .../PredefinedWidgets/CPULoad/CPULoad.tsx | 1 + .../PredefinedWidgets/Crashes/Crashes.tsx | 1 + .../DashboardSideMenu/DashboardSideMenu.tsx | 27 ++++++++++++++---- .../DashboardView/DashboardView.tsx | 17 ++++++----- .../components/WidgetWrapper/AlertButton.tsx | 28 +++++++++++++++++++ .../components/WidgetWrapper/WidgetIcon.tsx | 27 ++++++++++++++++++ .../WidgetWrapper/WidgetWrapper.tsx | 16 +++++++++-- frontend/app/components/Modal/index.tsx | 12 +++++++- frontend/app/mstore/dashboardStore.ts | 4 +++ 9 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 frontend/app/components/Dashboard/components/WidgetWrapper/AlertButton.tsx create mode 100644 frontend/app/components/Dashboard/components/WidgetWrapper/WidgetIcon.tsx diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx index 3ae354168..53356bf0d 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CPULoad/CPULoad.tsx @@ -20,6 +20,7 @@ function CPULoad(props: Props) { void } function DashboardSideMenu(props: Props) { - const { history, siteId } = props; + const { history, siteId, setShowAlerts } = props; const { hideModal, showModal } = useModal(); const { dashboardStore } = useStore(); const dashboardId = useObserver(() => dashboardStore.selectedDashboard?.dashboardId); @@ -54,9 +58,19 @@ function DashboardSideMenu(props: Props) { onClick={() => onItemClick(item)} className="group" leading = {( -
+
{item.isPublic &&
} - {
togglePinned(item)}>
} + {item.isPinned &&
} + {!item.isPinned && ( + +
togglePinned(item)} + > + +
+
+ )}
)} /> @@ -96,11 +110,12 @@ function DashboardSideMenu(props: Props) { id="menu-manage-alerts" title="Alerts" iconName="bell-plus" - // onClick={() => setShowAlerts(true)} + onClick={() => setShowAlerts(true)} />
)); } -export default withRouter(DashboardSideMenu); \ No newline at end of file +export default connect((state) => { +}, { setShowAlerts })(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 000a3c59c..b412dc1f2 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -11,6 +11,7 @@ import { useModal } from 'App/components/Modal'; import DashboardModal from '../DashboardModal'; import DashboardEditModal from '../DashboardEditModal'; import DateRange from 'Shared/DateRange'; +import AlertFormModal from 'App/components/Alerts/AlertFormModal'; interface Props { siteId: number; @@ -22,6 +23,7 @@ function DashboardView(props: Props) { const { siteId, dashboardId } = props; const { dashboardStore } = useStore(); const { hideModal, showModal } = useModal(); + const showAlertModal = useObserver(() => dashboardStore.showAlertModal); const loading = useObserver(() => dashboardStore.fetchingDashboard); const dashboards = useObserver(() => dashboardStore.dashboards); const dashboard: any = useObserver(() => dashboardStore.selectedDashboard); @@ -98,18 +100,11 @@ function DashboardView(props: Props) {
- {/* Options */}
@@ -120,6 +115,10 @@ function DashboardView(props: Props) { dashboardId={dashboardId} onEditHandler={onAddWidgets} /> + dashboardStore.updateKey('showAlertModal', false)} + />
diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/AlertButton.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/AlertButton.tsx new file mode 100644 index 000000000..78d858b3e --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/AlertButton.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import WidgetIcon from './WidgetIcon'; +import { init as initAlert } from 'Duck/alerts'; +import { useStore } from 'App/mstore'; + +interface Props { + seriesId: string; + initAlert: Function; +} +function AlertButton(props: Props) { + const { seriesId, initAlert } = props; + const { dashboardStore } = useStore(); + const onClick = () => { + initAlert({ query: { left: seriesId }}) + dashboardStore.updateKey('showAlertModal', true); + } + return ( + + ); +} + +export default connect(null, { initAlert })(AlertButton); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetIcon.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetIcon.tsx new file mode 100644 index 000000000..5ed7c2a45 --- /dev/null +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetIcon.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Icon } from 'UI'; +import { Tooltip } from 'react-tippy'; + +interface Props { + className: string + onClick: () => void + icon: string + tooltip: string +} +function WidgetIcon(props: Props) { + const { className, onClick, icon, tooltip } = props; + return ( + +
+ +
+
+ ); +} + +export default WidgetIcon; diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 2aadc8733..8f58e6691 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -4,12 +4,14 @@ 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 { 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'; import TemplateOverlay from './TemplateOverlay'; +import WidgetIcon from './WidgetIcon'; +import AlertButton from './AlertButton'; interface Props { className?: string; @@ -29,6 +31,7 @@ function WidgetWrapper(props: Props) { const { dashboardStore } = useStore(); const { isWidget = false, active = false, index = 0, moveListItem = null, isPreview = false, isTemplate = false, dashboardId, siteId } = props; const widget: any = useObserver(() => props.widget); + const isPredefined = widget.metricType === 'predefined'; const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -63,7 +66,7 @@ function WidgetWrapper(props: Props) { } const onChartClick = () => { - if (!isWidget || widget.metricType === 'predefined') return; + if (!isWidget || isPredefined) return; props.history.push(withSiteId(dashboardMetricDetails(dashboardId, widget.metricId),siteId)); } @@ -88,7 +91,14 @@ function WidgetWrapper(props: Props) { >

{widget.name}

{isWidget && ( -
+
+ {!isPredefined && ( + <> + +
+ + )} + { + if (e.keyCode === 27) { + this.hideModal(); + } + } + showModal = (component, props = {}) => { this.setState({ component, props }); + document.addEventListener('keydown', this.handleKeyDown); }; - hideModal = () => + hideModal = () => { this.setState({ component: null, props: {} }); + document.removeEventListener('keydown', this.handleKeyDown); + } state = { component: null, diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index fb33ac89e..ba8c23e58 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -31,6 +31,8 @@ export interface IDashboardSotre { fetchingDashboard: boolean sessionsLoading: boolean + showAlertModal: boolean + selectWidgetsByCategory: (category: string) => void toggleAllSelectedWidgets: (isSelected: boolean) => void removeSelectedWidgetByCategory(category: string): void @@ -93,6 +95,8 @@ export default class DashboardStore implements IDashboardSotre { fetchingDashboard: boolean = false sessionsLoading: boolean = false; + showAlertModal: boolean = false; + constructor() { makeAutoObservable(this, { drillDownFilter: observable.ref, From be7175c393f5f36f6bc3ef6a34fa057901a2afc8 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 18:57:29 +0200 Subject: [PATCH 170/294] feat(api): added chart to count_requests metric --- api/chalicelib/core/dashboard.py | 26 ++++++++++++++++++++++- ee/api/chalicelib/core/dashboard.py | 33 ++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 1afa10538..f90ebe275 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2760,11 +2760,18 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None, **args): + endTimestamp=TimeUTC.now(), value=None,density=20, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} pg_sub_query = __get_constraints(project_id=project_id, 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) if value is not None: pg_sub_query.append("pages.path = %(value)s") + pg_sub_query_chart.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) @@ -2774,5 +2781,22 @@ def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_ "endTimestamp": endTimestamp, "value": value, **__get_constraint_values(args)})) row = cur.fetchone() + pg_query = f"""WITH pages AS(SELECT pages.timestamp + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query)} + ) + SELECT generated_timestamp AS timestamp, + COUNT(pages.*) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT 1 + FROM pages + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp;""" + cur.execute(cur.mogrify(pg_query, {**params, **__get_constraint_values(args)})) + rows = cur.fetchall() + row["chart"]=rows row["unit"] = schemas.TemplatePredefinedUnits.count return helper.dict_to_camel_case(row) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 404aef795..7d1da004e 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2512,23 +2512,40 @@ def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(del 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) + endTimestamp=TimeUTC.now(), value=None, density=20, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) ch_sub_query += meta_condition if value is not None: ch_sub_query.append("pages.url_path = %(value)s") + ch_sub_query_chart.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]) + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + rows = ch.execute(query=ch_query, params=params) + result = rows[0] + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + 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_chart)} + GROUP BY timestamp + ORDER BY timestamp;""" + rows = ch.execute(query=ch_query, params={**params, **__get_constraint_values(args)}) + rows = __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, neutral={"value": 0}) + result["chart"] = rows + result["unit"] = schemas.TemplatePredefinedUnits.count + return helper.dict_to_camel_case(result) def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta_days=-1), From dc18805699ba614ce81ec8cddffcc36f4c621b58 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 14 Apr 2022 19:23:08 +0200 Subject: [PATCH 171/294] feat(ui) - dashboard - other widgets --- .../CallsErrors4xx/CallsErrors4xx.tsx | 1 + .../CallsErrors5xx/CallsErrors5xx.tsx | 1 + .../DomBuildingTime/DomBuildingTime.tsx | 4 +- .../ErrorsByOrigin/ErrorsByOrigin.tsx | 1 + .../ErrorsByType/ErrorsByType.tsx | 1 + .../ResourceLoadingTime.tsx | 4 +- .../ResponseTime/ResponseTime.tsx | 4 +- .../SessionsAffectedByJSErrors.tsx | 1 + .../SlowestResources/Chart.js | 15 +++ .../SlowestResources/CopyPath.js | 23 ++++ .../SlowestResources/ImageInfo.js | 27 +++++ .../SlowestResources/ResourceType.js | 12 ++ .../SlowestResources/SlowestResources.tsx | 81 +++++++++++++ .../SlowestResources/imageInfo.css | 52 ++++++++ .../SlowestResources/index.ts | 1 + .../SpeedIndexByLocation/Scale.js | 24 ++++ .../SpeedIndexByLocation.js | 112 ++++++++++++++++++ .../SpeedIndexByLocation/index.ts | 1 + .../SpeedIndexByLocation/scale.css | 11 ++ .../TimeToRender/TimeToRender.tsx | 4 +- .../DashboardSideMenu/DashboardSideMenu.tsx | 3 +- .../WidgetPredefinedChart.tsx | 8 +- 22 files changed, 379 insertions(+), 12 deletions(-) create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/Chart.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/CopyPath.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ImageInfo.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ResourceType.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/imageInfo.css create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/Scale.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/index.ts create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/scale.css diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx index e0f721078..afaaeb37d 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallsErrors4xx/CallsErrors4xx.tsx @@ -17,6 +17,7 @@ function CallsErrors4xx(props: Props) { <>
- + /> */}
diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx index 8e1a125f0..87fd42eb3 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx @@ -17,6 +17,7 @@ function ErrorsByOrigin(props: Props) { <>
- + /> */}
diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx index d1af7d2b4..a9ef6dcac 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx @@ -38,13 +38,13 @@ function ResponseTime(props: Props) { > <>
- + /> */}
diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx index a2206f2f9..0c077e747 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsAffectedByJSErrors/SessionsAffectedByJSErrors.tsx @@ -17,6 +17,7 @@ function SessionsAffectedByJSErrors(props: Props) { { + const colors = compare ? Styles.compareColors : Styles.colors; + return ( + + + + ); +} + +Chart.displayName = 'Chart'; + +export default Chart; diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/CopyPath.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/CopyPath.js new file mode 100644 index 000000000..6b7e709e7 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/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/SlowestResources/ImageInfo.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ImageInfo.js new file mode 100644 index 000000000..fed6b71b6 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ImageInfo.js @@ -0,0 +1,27 @@ +import { Popup } from 'UI'; +import cn from 'classnames'; +import styles from './imageInfo.css'; + +const supportedTypes = ['png', 'jpg', 'jpeg', 'svg']; + +const ImageInfo = ({ data }) => { + const canPreview = supportedTypes.includes(data.type); + return ( +
+ +
{data.name}
+
+ } + disabled={!canPreview} + content={ One of the slowest images } + /> +
+ ) +}; + +ImageInfo.displayName = 'ImageInfo'; + +export default ImageInfo; diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ResourceType.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ResourceType.js new file mode 100644 index 000000000..9803a050f --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/ResourceType.js @@ -0,0 +1,12 @@ +import React from 'react' +import cn from 'classnames' + +const ResourceType = ({ data : { type = 'js' }, compare }) => { + return ( +
+ { type.toUpperCase() } +
+ ) +} + +export default ResourceType diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx new file mode 100644 index 000000000..ca62855b0 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { NoContent } from 'UI'; +import { Styles, Table } from '../../common'; +import { List } from 'immutable'; +import { numberWithCommas } from 'App/utils'; + +import Chart from './Chart'; +import ImageInfo from './ImageInfo'; +import ResourceType from './ResourceType'; +import CopyPath from './CopyPath'; + +export const RESOURCE_OPTIONS = [ + { text: 'All', value: 'ALL', }, + { text: 'CSS', value: 'STYLESHEET', }, + { text: 'JS', value: 'SCRIPT', }, +]; + +const cols = [ + { + key: 'type', + title: 'Type', + Component: ResourceType, + className: 'text-center justify-center', + cellClass: 'ml-2', + width: '8%', + }, + { + key: 'name', + title: 'File Name', + Component: ImageInfo, + cellClass: '-ml-2', + width: '40%', + }, + { + key: 'avg', + title: 'Load Time', + toText: avg => `${ avg ? numberWithCommas(Math.trunc(avg)) : 0} ms`, + className: 'justify-center', + width: '15%', + }, + { + key: 'trend', + title: 'Trend', + Component: Chart, + width: '15%', + }, + { + key: 'copy-path', + title: '', + Component: CopyPath, + cellClass: 'invisible group-hover:visible text-right', + width: '15%', + } +]; + +interface Props { + data: any + metric?: any +} +function MissingResources(props: Props) { + const { data, metric } = props; + + return ( + +
+
+ + + ); +} + +export default MissingResources; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/imageInfo.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/imageInfo.css new file mode 100644 index 000000000..1de36b529 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/imageInfo.css @@ -0,0 +1,52 @@ +.name { + display: flex; + align-items: center; + + & > span { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 60%; + } + + & .label { + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} + +.hasPreview { + /* text-decoration: underline; */ + border-bottom: 1px dotted; + cursor: pointer; +} + +.imagePreview { + max-width: 200px; + max-height: 200px; +} + +.imageWrapper { + display: flex; + flex-flow: column; + align-items: center; + width: 40px; + text-align: center; + margin-right: 10px; + & > span { + height: 16px; + } + & .label { + font-size: 9px; + color: $gray-light; + } +} + +.popup { + background-color: #f5f5f5 !important; + &:before { + background-color: #f5f5f5 !important; + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/index.ts new file mode 100644 index 000000000..ca907e9f0 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/index.ts @@ -0,0 +1 @@ +export { default } from './SlowestResources' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/Scale.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/Scale.js new file mode 100644 index 000000000..2171c432e --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/Scale.js @@ -0,0 +1,24 @@ +import React from 'react' +import { Styles } from '../../common'; +import cn from 'classnames'; +import stl from './scale.css'; + +function Scale({ colors }) { + const lastIndex = (Styles.colors.length - 1) + return ( +
+ {colors.map((c, i) => ( +
+ { i === 0 &&
Slow
} + { i === lastIndex &&
Fast
} +
+ ))} +
+ ) +} + +export default Scale diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js new file mode 100644 index 000000000..411e47030 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js @@ -0,0 +1,112 @@ +import React, { useEffect } from 'react'; +import { NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import Scale from './Scale'; +import { threeLetter } from 'App/constants/countries'; +import { colorScale } from 'App/utils'; +import { observer } from 'mobx-react-lite'; +import * as DataMap from "datamaps"; +import { numberWithCommas } from 'App/utils'; + +// interface Props { +// metric?: any +// } +function SpeedIndexByLocation(props) { + const { metric } = props; + const wrapper: any = React.useRef(null); + let map: any = null; + + const getSeries = data => { + const series: any[] = []; + data.forEach(item => { + const d = [threeLetter[item.userCountry], Math.round(item.avg)] + series.push(d) + }) + + return series; + } + + useEffect(() => { + if (wrapper.current && !map && metric.data.chart.length > 0) { + const dataset = getDataset(); + map = new DataMap({ + element: wrapper.current, + fills: { defaultFill: '#E8E8E8' }, + data: dataset, + // responsive: true, + // height: null, //if not null, datamaps will grab the height of 'element' + // width: null, //if not null, datamaps will grab the width of 'element' + geographyConfig: { + borderColor: '#FFFFFF', + borderWidth: 0.5, + highlightBorderWidth: 1, + popupOnHover: true, + // don't change color on mouse hover + highlightFillColor: function(geo) { + return '#999999'; + // return geo['fillColor'] || '#F5F5F5'; + }, + // only change border + highlightBorderColor: '#B7B7B7', + // show desired information in tooltip + popupTemplate: function(geo, data) { + // don't show tooltip if country don't present in dataset + if (!data) { return ; } + // tooltip content + return ['
', + '', geo.properties.name, '', + 'Avg: ', numberWithCommas(data.numberOfThings), '', + '
'].join(''); + } + } + }); + } + }, []) + + // useEffect(() => { + // if (map) { + // map.updateChoropleth(getSeries(metric.data.chart), { reset: true}); + // } + // }, []) + + const getDataset = () => { + const { metric } = props; + const colors = Styles.colors; + + var dataset = {}; + const series = getSeries(metric.data.chart); + var onlyValues = series.map(function(obj){ return obj[1]; }); + const paletteScale = colorScale(onlyValues, [...colors].reverse()); + + // fill dataset in appropriate format + series.forEach(function(item){ + var iso = item[0], value = item[1]; + dataset[iso] = { numberOfThings: value, fillColor: paletteScale(value) }; + }); + return dataset; + } + + return ( + +
+ +
+ +
+ + ); +} + +export default observer(SpeedIndexByLocation); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/index.ts new file mode 100644 index 000000000..1cbdfe2f8 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/index.ts @@ -0,0 +1 @@ +export { default } from './SpeedIndexByLocation' \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/scale.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/scale.css new file mode 100644 index 000000000..5aa34f966 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/scale.css @@ -0,0 +1,11 @@ +.bars { + & div:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; + } + + & div:last-child { + border-bottom-left-radius: 3px; + border-bottom-right-radius: 3px; + } +} \ 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 7e3b928bb..e0da69b8b 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx @@ -38,13 +38,13 @@ function TimeToRender(props: Props) { > <>
- + /> */}
diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 0d04ad363..6cd0c19e1 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -117,5 +117,4 @@ function DashboardSideMenu(props: Props) { )); } -export default connect((state) => { -}, { setShowAlerts })(withRouter(DashboardSideMenu)); \ No newline at end of file +export default connect(null, { setShowAlerts })(withRouter(DashboardSideMenu)); \ No newline at end of file diff --git a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx index 151825b80..e61510760 100644 --- a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx @@ -24,6 +24,8 @@ import MissingResources from 'App/components/Dashboard/Widgets/PredefinedWidgets import ResourceLoadedVsResponseEnd from 'App/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadedVsResponseEnd'; import SessionsPerBrowser from 'App/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser'; import CallWithErrors from '../../Widgets/PredefinedWidgets/CallWithErrors'; +import SpeedIndexByLocation from '../../Widgets/PredefinedWidgets/SpeedIndexByLocation'; +import SlowestResources from '../../Widgets/PredefinedWidgets/SlowestResources'; interface Props { data: any; @@ -54,7 +56,8 @@ function WidgetPredefinedChart(props: Props) { // PERFORMANCE // case 'impacted_sessions_by_slow_pages': // case 'pages_response_time_distribution': - // case 'speed_location': + case 'speed_location': + return case 'cpu': return case 'crashes': @@ -85,7 +88,8 @@ function WidgetPredefinedChart(props: Props) { return case 'resources_loading_time': return - // case 'slowest_resources': + case 'slowest_resources': + return default: return
Widget not supported
From 32154e7b8220766a74b5cdb4542d997c37299499 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 19:36:05 +0200 Subject: [PATCH 172/294] feat(db): dashboard changed config for pages_response_time_distribution metric --- ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 2 +- ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql | 2 +- scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql | 2 +- scripts/helm/db/init_dbs/postgresql/init_schema.sql | 2 +- 4 files changed, 4 insertions(+), 4 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 ba00ea335..628ddc8b3 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 @@ -270,7 +270,7 @@ VALUES ('Captured sessions', 'overview', '{ "position": 0 }', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), ('Pages Response Time Distribution', 'performance', '{ - "col": 2, + "col": 4, "row": 2, "position": 0 }', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), 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 bc8d65f93..6b67499bd 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1474,7 +1474,7 @@ VALUES ('Captured sessions', 'overview', '{ "position": 0 }', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), ('Pages Response Time Distribution', 'performance', '{ - "col": 2, + "col": 4, "row": 2, "position": 0 }', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), 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 9e6421b07..3f853a597 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 @@ -107,7 +107,7 @@ VALUES ('Captured sessions', 'overview', '{"col":1,"row":1,"position":0}', true, ('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'), + ('Pages Response Time Distribution', 'performance', '{"col":4,"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":4,"row":2,"position":0}', true, true, true, 'slowest_resources', 'predefined', 'table'), diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 7f4d864fe..cc02388f3 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -1264,7 +1264,7 @@ VALUES ('Captured sessions', 'overview', '{ "position": 0 }', true, true, true, 'pages_response_time', 'predefined', 'areaChart'), ('Pages Response Time Distribution', 'performance', '{ - "col": 2, + "col": 4, "row": 2, "position": 0 }', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), From a8fb2f671b718db4a3ece8c85262ba81f2b4bf90 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 19:52:07 +0200 Subject: [PATCH 173/294] feat(api): added chart to avg_first_contentful_pixel metric --- api/chalicelib/core/dashboard.py | 40 +++++++++++++++++++++++++++-- ee/api/chalicelib/core/dashboard.py | 26 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index f90ebe275..959d84b34 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2533,6 +2533,9 @@ def get_page_metrics_avg_first_contentful_pixel(project_id, startTimestamp=TimeU 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]) + results["chart"] = __get_page_metrics_avg_first_contentful_pixel_chart(cur, project_id, startTimestamp, + endTimestamp, **args) + diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2562,6 +2565,39 @@ def __get_page_metrics_avg_first_contentful_pixel(cur, project_id, startTimestam return rows +def __get_page_metrics_avg_first_contentful_pixel_chart(cur, project_id, startTimestamp, endTimestamp, density=20, + **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + 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_sub_query_subset.append("pages.first_contentful_paint_time > 0") + + pg_query = f"""WITH pages AS(SELECT pages.first_contentful_paint_time, pages.timestamp + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_subset)} + ) + SELECT generated_timestamp AS timestamp, + COALESCE(AVG(pages.first_contentful_paint_time),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT pages.first_contentful_paint_time + FROM pages + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp;""" + cur.execute(cur.mogrify(pg_query, {**params, **__get_constraint_values(args)})) + 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: @@ -2760,7 +2796,7 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None,density=20, **args): + endTimestamp=TimeUTC.now(), value=None, density=20, **args): step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp} @@ -2797,6 +2833,6 @@ def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_ ORDER BY generated_timestamp;""" cur.execute(cur.mogrify(pg_query, {**params, **__get_constraint_values(args)})) rows = cur.fetchall() - row["chart"]=rows + row["chart"] = rows row["unit"] = schemas.TemplatePredefinedUnits.count return helper.dict_to_camel_case(row) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 7d1da004e..0a314aa3f 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2388,6 +2388,8 @@ def get_page_metrics_avg_first_contentful_pixel(project_id, startTimestamp=TimeU 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]) + results["chart"] = __get_page_metrics_avg_first_contentful_pixel_chart(ch, project_id, startTimestamp, + endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2395,6 +2397,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 @@ -2413,6 +2416,29 @@ def __get_page_metrics_avg_first_contentful_pixel(ch, project_id, startTimestamp return rows +def __get_page_metrics_avg_first_contentful_pixel_chart(ch, project_id, startTimestamp, endTimestamp, density=20, + **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + 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_chart)} + GROUP BY timestamp + ORDER BY timestamp;""" + rows = ch.execute(query=ch_query, params={**params, **__get_constraint_values(args)}) + rows = __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, neutral={"value": 0}) + return rows + + def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): results = {} From e35cc072e8f616bd20bb33d0985d7df1183c5d88 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 19:53:51 +0200 Subject: [PATCH 174/294] fix(certmanager): check domain ip Signed-off-by: rjshrjndrn --- scripts/helmcharts/certmanager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/helmcharts/certmanager.sh b/scripts/helmcharts/certmanager.sh index 64ba77b14..daab4d661 100644 --- a/scripts/helmcharts/certmanager.sh +++ b/scripts/helmcharts/certmanager.sh @@ -14,8 +14,8 @@ fatal() } read -p "enter openreplay domain name: " domain -nslookup domain > /dev/null || { - fatal "Domain name doesn't have ip associated with it. Please check your DNS record." +nslookup $domain > /dev/null || { + fatal "Domain name does not have ip associated with it. Please check your DNS record." } # Reading email address for ssl certificate From 07b38c8971058e26add705830cb73346b08b52c3 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 20:10:26 +0200 Subject: [PATCH 175/294] chore(helm): removed deprecated Nginx config Signed-off-by: rjshrjndrn --- scripts/helmcharts/vars.yaml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/scripts/helmcharts/vars.yaml b/scripts/helmcharts/vars.yaml index 1e8f268d3..67c1991b0 100644 --- a/scripts/helmcharts/vars.yaml +++ b/scripts/helmcharts/vars.yaml @@ -78,7 +78,7 @@ global: # if you're using one node installation, where # you're using local s3, make sure these variables # are same as minio.global.minio.accesskey and secretKey - accessKey: "changeMeMinioAccessKey" + accessKey: "YkkPAPYjogRlicqvCuNSHkfsdGtCCq" secretKey: "changeMeMinioPassword" email: emailHost: '' @@ -92,7 +92,7 @@ global: emailFrom: 'OpenReplay' enterpriseEditionLicense: "" - domainName: "" + domainName: "foss.openreplay.com" # If there is multiple nodes in the kubernetes cluster, # we'll have to create a NFS share PVC for both the containers to share data. @@ -132,14 +132,3 @@ chalice: # requests: # cpu: 512m # memory: 2056Mi - -## Changes to nginx -# -# nginx-ingress: -# # Key and certificate files must be named site.key and site.crt -# # and copied to ../openreplay/files/ -# sslKey: site.key -# sslCert: site.crt -# # Redirecting http to https -# customServerConfigs: | -# return 301 https://$host$request_uri; From 49f90ba11f42e88d7d5047fb09e6e37c535a1d75 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 20:15:16 +0200 Subject: [PATCH 176/294] feat(api): added chart to avg_first_contentful_pixel metric --- api/chalicelib/core/dashboard.py | 34 ++++++++++++++++++++++++++++- ee/api/chalicelib/core/dashboard.py | 26 ++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 959d84b34..88817da28 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2535,7 +2535,6 @@ def get_page_metrics_avg_first_contentful_pixel(project_id, startTimestamp=TimeU results = helper.dict_to_camel_case(rows[0]) results["chart"] = __get_page_metrics_avg_first_contentful_pixel_chart(cur, project_id, startTimestamp, endTimestamp, **args) - diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2603,6 +2602,9 @@ def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(d 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) + results["chart"] = __get_user_activity_avg_visited_pages_chart(cur, project_id, startTimestamp, + endTimestamp, **args) + diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2629,6 +2631,36 @@ def __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTi return row +def __get_user_activity_avg_visited_pages_chart(cur, project_id, startTimestamp, endTimestamp,density=20, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + 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="sessions", time_column="start_ts", + duration=False) + pg_sub_query_subset.append("sessions.duration IS NOT NULL") + + pg_query = f"""WITH sessions AS(SELECT sessions.pages_count, sessions.start_ts + FROM public.sessions + WHERE {" AND ".join(pg_sub_query_subset)} + ) + SELECT generated_timestamp AS timestamp, + COALESCE(AVG(sessions.pages_count),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT sessions.pages_count + FROM sessions + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS sessions ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp;""" + cur.execute(cur.mogrify(pg_query, {**params, **__get_constraint_values(args)})) + rows = cur.fetchall() + return rows + + 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: diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 0a314aa3f..60d9b2ae3 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2450,6 +2450,9 @@ def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(d for key in results: if isnan(results[key]): results[key] = 0 + results["chart"] = __get_user_activity_avg_visited_pages_chart(ch, project_id, startTimestamp, + endTimestamp, **args) + diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2458,6 +2461,7 @@ def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(d 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.count return results @@ -2478,6 +2482,28 @@ def __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTi return rows +def __get_user_activity_avg_visited_pages_chart(ch, project_id, startTimestamp, endTimestamp, density=20, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + ch_sub_query_chart = __get_basic_constraints(table_name="sessions", round_start=True, data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(sessions.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + COALESCE(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_chart)} + GROUP BY timestamp + ORDER BY timestamp;""" + rows = ch.execute(query=ch_query, params={**params, **__get_constraint_values(args)}) + rows = __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, neutral={"value": 0}) + return rows + + def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), **args): results = {} From 9a0415db67dd902e7d8028cb6cb5e2b31d3dca95 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 20:29:35 +0200 Subject: [PATCH 177/294] feat(api): added chart to avg_time_to_interactive metric --- api/chalicelib/core/dashboard.py | 34 ++++++++++++++++++++++------- ee/api/chalicelib/core/dashboard.py | 34 ++++++++++++++++++++++------- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 88817da28..360cac96c 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2631,7 +2631,7 @@ def __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTi return row -def __get_user_activity_avg_visited_pages_chart(cur, project_id, startTimestamp, endTimestamp,density=20, **args): +def __get_user_activity_avg_visited_pages_chart(cur, project_id, startTimestamp, endTimestamp, density=20, **args): step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp} @@ -2776,7 +2776,7 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no ORDER BY generated_timestamp ASC;""" cur.execute(cur.mogrify(pg_query, params)) rows = cur.fetchall() - row["chart"] = helper.list_to_camel_case(rows), + row["chart"] = helper.list_to_camel_case(rows) row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) @@ -2805,24 +2805,42 @@ def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(de def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None, **args): + endTimestamp=TimeUTC.now(), value=None, density=20, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, + chart=True, data=args) if value is not None: pg_sub_query.append("pages.path = %(value)s") + pg_sub_query_chart.append("pages.path = %(value)s") with pg_client.PostgresClient() as cur: - pg_query = f"""SELECT COALESCE(AVG(pages.time_to_interactive), 0) AS value + pg_query = f"""SELECT COALESCE(AVG(NULLIF(pages.time_to_interactive,0)), 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)})) + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + pg_query = f"""SELECT generated_timestamp AS timestamp, + COALESCE(AVG(NULLIF(pages.time_to_interactive,0)),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT time_to_interactive + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp ASC;""" + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + row["chart"] = helper.list_to_camel_case(rows) row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 60d9b2ae3..f94cd99b9 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2678,20 +2678,38 @@ def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(de 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) + endTimestamp=TimeUTC.now(), value=None, density=20, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density) + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) ch_sub_query += meta_condition if value is not None: ch_sub_query.append("pages.url_path = %(value)s") + ch_sub_query_chart.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]) + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + rows = ch.execute(query=ch_query, params=params) + results = rows[0] + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, + COALESCE(AVG(NULLIF(pages.time_to_interactive ,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_chart)} AND isNotNull(pages.time_to_interactive) AND pages.time_to_interactive >0 + GROUP BY timestamp + ORDER BY timestamp;;""" + rows = ch.execute(query=ch_query, params=params) + results["chart"] = helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, + neutral={"value": 0})) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond + return helper.dict_to_camel_case(results) From efa7ef826132e9676f7bda6f6d381785f45d69f1 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 14 Apr 2022 20:39:07 +0200 Subject: [PATCH 178/294] feat(api): added chart to avg_till_first_byte metric --- api/chalicelib/core/dashboard.py | 28 +++++++++++++++++++----- ee/api/chalicelib/core/dashboard.py | 34 ++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 360cac96c..47b4237b0 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2782,11 +2782,15 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None, **args): + endTimestamp=TimeUTC.now(), value=None, density=20, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, + chart=True, data=args) if value is not None: pg_sub_query.append("pages.path = %(value)s") + pg_sub_query_chart.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 @@ -2795,11 +2799,25 @@ def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(de 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)})) + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + pg_query = f"""SELECT generated_timestamp AS timestamp, + COALESCE(AVG(pages.ttfb),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT ttfb + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_chart)} AND pages.ttfb > 0 + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp ASC;""" + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + row["chart"] = helper.list_to_camel_case(rows) row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index f94cd99b9..092b7c536 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2658,23 +2658,41 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no 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) + endTimestamp=TimeUTC.now(), value=None, density=20, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density) + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) ch_sub_query += meta_condition if value is not None: ch_sub_query.append("pages.url_path = %(value)s") + ch_sub_query_chart.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]) + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + rows = ch.execute(query=ch_query, params=params) + results = rows[0] + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, + COALESCE(AVG(NULLIF(pages.ttfb ,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_chart)} AND isNotNull(pages.ttfb) AND pages.ttfb>0 + GROUP BY timestamp + ORDER BY timestamp;""" + rows = ch.execute(query=ch_query, params=params) + results["chart"] = helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, + neutral={"value": 0})) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond + return helper.dict_to_camel_case(results) def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.now(delta_days=-1), From 7c7aee64591cd1c7492505d63689b6b99bfd07e6 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Thu, 14 Apr 2022 20:40:57 +0200 Subject: [PATCH 179/294] chore(nginx): disable ssl redirection by default Signed-off-by: rjshrjndrn --- scripts/helmcharts/vars.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/helmcharts/vars.yaml b/scripts/helmcharts/vars.yaml index 67c1991b0..7f5aaf424 100644 --- a/scripts/helmcharts/vars.yaml +++ b/scripts/helmcharts/vars.yaml @@ -58,10 +58,10 @@ ingress-nginx: &ingress-nginx forwarded-for-header: "proxy_protocol" # Ref: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#max-worker-connections max-worker-connections: 0 - # If you want the ssl offload in LB layer - # Uncomment the following line - # ssl-redirect: false - # force-ssl-redirect: false + # If you want the ssl redirection + # Comment the following line + ssl-redirect: false + force-ssl-redirect: false # Application specific variables global: From 7076181b0739c71484ed6564ce9f5ac927a8e956 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 10:19:13 +0200 Subject: [PATCH 180/294] feat(api): added chart to avg_first_paint metric --- api/chalicelib/core/dashboard.py | 24 +++++++++++++++++--- ee/api/chalicelib/core/dashboard.py | 35 ++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 47b4237b0..ff3489432 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2716,11 +2716,15 @@ def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(del def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None, **args): + endTimestamp=TimeUTC.now(), value=None,density=20, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, + chart=True, data=args) if value is not None: pg_sub_query.append("pages.path = %(value)s") + pg_sub_query_chart.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 @@ -2729,11 +2733,25 @@ def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta 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, + params={"step_size":step_size,"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, - "value": value, **__get_constraint_values(args)})) + "value": value, **__get_constraint_values(args)} + cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + pg_query = f"""SELECT generated_timestamp AS timestamp, + COALESCE(AVG(pages.first_paint_time),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT first_paint_time + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_chart)} AND pages.first_paint_time > 0 + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp ASC;""" + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + row["chart"] = helper.list_to_camel_case(rows) row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 092b7c536..f3daa6e67 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2601,23 +2601,42 @@ def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_ 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) + endTimestamp=TimeUTC.now(), value=None, density=20, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density) + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) ch_sub_query += meta_condition if value is not None: ch_sub_query.append("pages.url_path = %(value)s") + ch_sub_query_chart.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]) + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + rows = ch.execute(query=ch_query, params=params) + results = rows[0] + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, + 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_chart)} AND isNotNull(pages.first_paint) AND pages.first_paint>0 + GROUP BY timestamp + ORDER BY timestamp;;""" + rows = ch.execute(query=ch_query, params=params) + results["chart"] = helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, + neutral={"value": 0})) + + results["unit"] = schemas.TemplatePredefinedUnits.millisecond + return helper.dict_to_camel_case(results) def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.now(delta_days=-1), From 61ef427b4fe826cf600d4c3c7be896e88c39a8f1 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 10:33:34 +0200 Subject: [PATCH 181/294] feat(api): added chart to avg_first_paint metric --- api/chalicelib/core/dashboard.py | 24 +++++++++++++++++--- ee/api/chalicelib/core/dashboard.py | 35 +++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index ff3489432..0c143f98a 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2693,11 +2693,15 @@ def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, en def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None, **args): + endTimestamp=TimeUTC.now(), value=None,density=20, **args): + step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) pg_sub_query = __get_constraints(project_id=project_id, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, + chart=True, data=args) if value is not None: pg_sub_query.append("pages.path = %(value)s") + pg_sub_query_chart.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 @@ -2706,11 +2710,25 @@ def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(del 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, + params={"step_size":step_size,"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, - "value": value, **__get_constraint_values(args)})) + "value": value, **__get_constraint_values(args)} + cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + pg_query = f"""SELECT generated_timestamp AS timestamp, + 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 first_paint_time + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_chart)} AND pages.response_time > 0 + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp ASC;""" + cur.execute(cur.mogrify(pg_query, params)) + rows = cur.fetchall() + row["chart"] = helper.list_to_camel_case(rows) row["unit"] = schemas.TemplatePredefinedUnits.millisecond return helper.dict_to_camel_case(row) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index f3daa6e67..c9a33f489 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2544,23 +2544,40 @@ def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, en 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) + endTimestamp=TimeUTC.now(), value=None, density=20, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + ch_sub_query = __get_basic_constraints(table_name="pages", data=args) ch_sub_query += meta_condition if value is not None: ch_sub_query.append("pages.url_path = %(value)s") + ch_sub_query_chart.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]) + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} + rows = ch.execute(query=ch_query, params=params) + results = rows[0] + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + COUNT(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)} AND isNotNull(pages.response_time) AND pages.response_time>0 + GROUP BY timestamp + ORDER BY timestamp;""" + rows = ch.execute(query=ch_query, params={**params, **__get_constraint_values(args)}) + rows = __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, neutral={"value": 0}) + results["chart"] = rows + results["unit"] = schemas.TemplatePredefinedUnits.millisecond + return helper.dict_to_camel_case(results) def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -2596,7 +2613,7 @@ def get_top_metrics_count_requests(project_id, startTimestamp=TimeUTC.now(delta_ end_time=endTimestamp, density=density, neutral={"value": 0}) result["chart"] = rows - result["unit"] = schemas.TemplatePredefinedUnits.count + result["unit"] = schemas.TemplatePredefinedUnits.count return helper.dict_to_camel_case(result) From 0ca6e62627c75dda813552581b2d429322f683fa Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 10:47:25 +0200 Subject: [PATCH 182/294] feat(api): added chart to avg_session_duration metric --- api/chalicelib/core/dashboard.py | 53 +++++++++++++++++++++++------ ee/api/chalicelib/core/dashboard.py | 29 ++++++++++++++++ 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 0c143f98a..e57bc9b26 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2666,6 +2666,9 @@ def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.no 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) + results["chart"] = __get_user_activity_avg_session_duration_chart(cur, project_id, startTimestamp, + endTimestamp, **args) + diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2692,8 +2695,38 @@ def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, en return row +def __get_user_activity_avg_session_duration_chart(cur, project_id, startTimestamp, endTimestamp, density=20, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density, factor=1) + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + pg_sub_query_subset = __get_constraints(project_id=project_id, data=args) + pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=False, project=False, + chart=True, data=args, main_table="sessions", time_column="start_ts", + duration=False) + pg_sub_query_subset.append("sessions.duration IS NOT NULL") + pg_sub_query_subset.append("sessions.duration > 0") + + pg_query = f"""WITH sessions AS(SELECT sessions.duration, sessions.start_ts + FROM public.sessions + WHERE {" AND ".join(pg_sub_query_subset)} + ) + SELECT generated_timestamp AS timestamp, + COALESCE(AVG(sessions.duration),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT sessions.duration + FROM sessions + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS sessions ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp;""" + cur.execute(cur.mogrify(pg_query, {**params, **__get_constraint_values(args)})) + rows = cur.fetchall() + return rows + + def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None,density=20, **args): + endTimestamp=TimeUTC.now(), value=None, density=20, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) pg_sub_query = __get_constraints(project_id=project_id, data=args) pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, @@ -2710,10 +2743,10 @@ def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(del AND pages.timestamp >= %(startTimestamp)s AND pages.timestamp < %(endTimestamp)s AND pages.response_time > 0;""" - params={"step_size":step_size,"project_id": project_id, - "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, - "value": value, **__get_constraint_values(args)} + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() pg_query = f"""SELECT generated_timestamp AS timestamp, @@ -2734,7 +2767,7 @@ def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(del def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta_days=-1), - endTimestamp=TimeUTC.now(), value=None,density=20, **args): + endTimestamp=TimeUTC.now(), value=None, density=20, **args): step_size = __get_step_size(startTimestamp, endTimestamp, density, factor=1) pg_sub_query = __get_constraints(project_id=project_id, data=args) pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, @@ -2751,10 +2784,10 @@ def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta AND pages.timestamp >= %(startTimestamp)s AND pages.timestamp < %(endTimestamp)s AND pages.first_paint_time > 0;""" - params={"step_size":step_size,"project_id": project_id, - "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, - "value": value, **__get_constraint_values(args)} + params = {"step_size": step_size, "project_id": project_id, + "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, + "value": value, **__get_constraint_values(args)} cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() pg_query = f"""SELECT generated_timestamp AS timestamp, diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index c9a33f489..86f7b3a36 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -2515,6 +2515,8 @@ def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.no for key in results: if isnan(results[key]): results[key] = 0 + results["chart"] = __get_user_activity_avg_session_duration_chart(ch, project_id, startTimestamp, + endTimestamp, **args) diff = endTimestamp - startTimestamp endTimestamp = startTimestamp startTimestamp = endTimestamp - diff @@ -2523,6 +2525,7 @@ def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.no 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 @@ -2530,6 +2533,8 @@ def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, en ch_sub_query = __get_basic_constraints(table_name="sessions", data=args) meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition + ch_sub_query.append("isNotNull(sessions.duration)") + ch_sub_query.append("sessions.duration>0") ch_query = f"""\ SELECT COALESCE(AVG(NULLIF(sessions.duration,0)),0) AS value @@ -2543,6 +2548,30 @@ def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, en return rows +def __get_user_activity_avg_session_duration_chart(ch, project_id, startTimestamp, endTimestamp, density=20, **args): + step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) + ch_sub_query_chart = __get_basic_constraints(table_name="sessions", round_start=True, data=args) + meta_condition = __get_meta_constraint(args) + ch_sub_query_chart += meta_condition + ch_sub_query_chart.append("isNotNull(sessions.duration)") + ch_sub_query_chart.append("sessions.duration>0") + params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp} + + ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(sessions.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, + COALESCE(AVG(sessions.duration),0) 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 + ORDER BY timestamp;""" + + rows = ch.execute(query=ch_query, params={**params, **__get_constraint_values(args)}) + rows = __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, neutral={"value": 0}) + return rows + + def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), value=None, density=20, **args): step_size = __get_step_size(endTimestamp=endTimestamp, startTimestamp=startTimestamp, density=density) From 01d22f2cd0abd798f15f19fbf29e049483fde909 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 11:04:02 +0200 Subject: [PATCH 183/294] feat(db): dashboard fixed missing attributes for widgets --- api/chalicelib/core/dashboard.py | 4 ++- ee/api/chalicelib/core/dashboard.py | 44 ++++++++++++++++++----------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index e57bc9b26..0b7765b76 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -2331,6 +2331,7 @@ def __get_application_activity_avg_page_load_time(cur, project_id, startTimestam cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return row @@ -2400,6 +2401,7 @@ def __get_application_activity_avg_request_load_time(cur, project_id, startTimes "endTimestamp": endTimestamp, **__get_constraint_values(args)})) row = cur.fetchone() + row["unit"] = schemas.TemplatePredefinedUnits.millisecond return row @@ -2753,7 +2755,7 @@ def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(del 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 first_paint_time + SELECT response_time FROM events.pages INNER JOIN public.sessions USING (session_id) WHERE {" AND ".join(pg_sub_query_chart)} AND pages.response_time > 0 ) AS pages ON (TRUE) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 86f7b3a36..5d25207e5 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -203,7 +203,7 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) count = count[0]["count"] results["progress"] = helper.__progress(old_val=count, new_val=results["value"]) - + results["unit"] = schemas.TemplatePredefinedUnits.count return results @@ -249,15 +249,15 @@ def get_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimesta return results -def __count_distinct_errors(cur, project_id, startTimestamp, endTimestamp, ch_sub_query, meta=False, **args): +def __count_distinct_errors(ch, project_id, startTimestamp, endTimestamp, ch_sub_query, meta=False, **args): ch_query = f"""\ SELECT COUNT(DISTINCT errors.message) AS count FROM errors {"INNER JOIN sessions_metadata USING(session_id)" if meta else ""} WHERE {" AND ".join(ch_sub_query)};""" - count = cur.execute(query=ch_query, - params={"project_id": project_id, "startTimestamp": startTimestamp, - "endTimestamp": endTimestamp, **__get_constraint_values(args)}) + count = ch.execute(query=ch_query, + params={"project_id": project_id, "startTimestamp": startTimestamp, + "endTimestamp": endTimestamp, **__get_constraint_values(args)}) if count is not None and len(count) > 0: return count[0]["count"] @@ -431,7 +431,7 @@ def get_user_activity(project_id, startTimestamp=TimeUTC.now(delta_days=-1), return results -def __get_user_activity(cur, project_id, startTimestamp, endTimestamp, **args): +def __get_user_activity(ch, 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 @@ -444,7 +444,7 @@ def __get_user_activity(cur, project_id, startTimestamp, endTimestamp, **args): params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} - rows = cur.execute(query=ch_query, params=params) + rows = ch.execute(query=ch_query, params=params) return rows @@ -952,7 +952,8 @@ def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=- return {"value": avg, "chart": __complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, - density=density, neutral={"value": 0})} + density=density, neutral={"value": 0}), + "unit": schemas.TemplatePredefinedUnits.millisecond} def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1097,7 +1098,8 @@ def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1 return {"value": avg, "chart": __complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, - density=density, neutral={"value": 0})} + density=density, neutral={"value": 0}), + "unit": schemas.TemplatePredefinedUnits.millisecond} def get_pages_response_time_distribution(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1295,7 +1297,8 @@ def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), 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})} + neutral={"value": 0}), + "unit": schemas.TemplatePredefinedUnits.millisecond} def get_impacted_sessions_by_slow_pages(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1359,7 +1362,8 @@ def get_memory_consumption(project_id, startTimestamp=TimeUTC.now(delta_days=-1) "chart": helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, - neutral={"value": 0}))} + neutral={"value": 0})), + "unit": schemas.TemplatePredefinedUnits.memory} def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1390,7 +1394,8 @@ def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "chart": helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, - neutral={"value": 0}))} + neutral={"value": 0})), + "unit": schemas.TemplatePredefinedUnits.percentage} def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -1421,7 +1426,8 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "chart": helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, density=density, - neutral={"value": 0}))} + neutral={"value": 0})), + "unit": schemas.TemplatePredefinedUnits.frame} def __get_crashed_sessions_ids(project_id, startTimestamp, endTimestamp): @@ -2104,6 +2110,7 @@ def get_application_activity_avg_page_load_time(project_id, startTimestamp=TimeU 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"]) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2182,6 +2189,7 @@ def get_application_activity_avg_image_load_time(project_id, startTimestamp=Time 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"]) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2256,6 +2264,7 @@ def get_application_activity_avg_request_load_time(project_id, startTimestamp=Ti 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"]) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results @@ -2465,7 +2474,7 @@ def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(d return results -def __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTimestamp, **args): +def __get_user_activity_avg_visited_pages(ch, 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 @@ -2477,7 +2486,7 @@ def __get_user_activity_avg_visited_pages(cur, project_id, startTimestamp, endTi params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} - rows = cur.execute(query=ch_query, params=params) + rows = ch.execute(query=ch_query, params=params) return rows @@ -2529,7 +2538,7 @@ def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.no return results -def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, endTimestamp, **args): +def __get_user_activity_avg_session_duration(ch, 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 @@ -2543,7 +2552,7 @@ def __get_user_activity_avg_session_duration(cur, project_id, startTimestamp, en params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} - rows = cur.execute(query=ch_query, params=params) + rows = ch.execute(query=ch_query, params=params) return rows @@ -2719,6 +2728,7 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no end_time=endTimestamp, density=density, neutral={"value": 0})) + results["unit"] = schemas.TemplatePredefinedUnits.millisecond return results From a2d17d930d01d06b7878760ac694255de3bc9e9e Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 11:18:04 +0200 Subject: [PATCH 184/294] feat(api): removed projects from login and client response --- api/routers/core_dynamic.py | 4 ---- api/routers/subs/dashboard.py | 3 ++- ee/api/routers/core_dynamic.py | 2 -- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/api/routers/core_dynamic.py b/api/routers/core_dynamic.py index c149266b5..790e65340 100644 --- a/api/routers/core_dynamic.py +++ b/api/routers/core_dynamic.py @@ -51,8 +51,6 @@ def login(data: schemas.UserLoginSchema = Body(...)): c = tenants.get_by_tenant_id(tenant_id) c.pop("createdAt") - c["projects"] = projects.get_projects(tenant_id=tenant_id, recording_state=True, recorded=True, - stack_integrations=True, version=True) c["smtp"] = helper.has_smtp() c["iceServers"] = assist.get_ice_servers() r["smtp"] = c["smtp"] @@ -219,8 +217,6 @@ def get_client(context: schemas.CurrentContext = Depends(OR_context)): r = tenants.get_by_tenant_id(context.tenant_id) if r is not None: r.pop("createdAt") - r["projects"] = projects.get_projects(tenant_id=context.tenant_id, recording_state=True, recorded=True, - stack_integrations=True, version=True) return { 'data': r } diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index e2d4ba268..029127cbd 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -342,7 +342,8 @@ 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.TemplatePredefinedKeys.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} diff --git a/ee/api/routers/core_dynamic.py b/ee/api/routers/core_dynamic.py index 52fc737e4..561100ff2 100644 --- a/ee/api/routers/core_dynamic.py +++ b/ee/api/routers/core_dynamic.py @@ -56,8 +56,6 @@ def login(data: schemas.UserLoginSchema = Body(...)): c = tenants.get_by_tenant_id(tenant_id) c.pop("createdAt") - c["projects"] = projects.get_projects(tenant_id=tenant_id, recording_state=True, recorded=True, - stack_integrations=True, version=True, user_id=r["id"]) c["smtp"] = helper.has_smtp() c["iceServers"] = assist.get_ice_servers() r["smtp"] = c["smtp"] From 9cc4cebc5e34bf84993a9709678fb5d4f84dfed3 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 11:50:17 +0200 Subject: [PATCH 185/294] feat(ui) - dashboard - other widgets --- .../ResponseTimeDistribution.tsx | 128 ++++++++++++++++++ .../ResponseTimeDistribution/index.ts | 1 + .../SessionsImpactedBySlowRequests.tsx | 2 +- .../SpeedIndexByLocation.js | 12 +- .../DashboardSideMenu/DashboardSideMenu.tsx | 7 +- .../WidgetPredefinedChart.tsx | 7 +- .../WidgetWrapper/TemplateOverlay.tsx | 4 - .../app/components/ui/ItemMenu/ItemMenu.js | 35 +++-- frontend/app/components/ui/Loader/Loader.js | 2 +- frontend/app/constants/countries.js | 2 +- frontend/app/mstore/types/widget.ts | 4 +- 11 files changed, 170 insertions(+), 34 deletions(-) create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/ResponseTimeDistribution.tsx create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/index.ts diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/ResponseTimeDistribution.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/ResponseTimeDistribution.tsx new file mode 100644 index 000000000..4cfb032e2 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/ResponseTimeDistribution.tsx @@ -0,0 +1,128 @@ +import React from 'react'; +import { Loader, NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import { + ComposedChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, + XAxis, YAxis, ReferenceLine, Tooltip, Legend +} from 'recharts'; + + +const PercentileLine = props => { + const { + viewBox: { x, y }, + xoffset, + yheight, + height, + label + } = props; + return ( + + + + {label} + + + ); +}; + +interface Props { + data: any + metric?: any +} +function ResponseTimeDistribution(props: Props) { + const { data, metric } = props; + const colors = Styles.colors; + + return ( + +
+ +
+
+ + + + + + + 'Page Response Time: ' + val} /> + { metric.data.percentiles.map((item, i) => ( + + } + allowDecimals={false} + x={item.responseTime} + strokeWidth={0} + strokeOpacity={1} + /> + ))} + + + + + + + + + + + +
+
+ ); +} + +export default ResponseTimeDistribution; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/index.ts b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/index.ts new file mode 100644 index 000000000..163efa255 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/index.ts @@ -0,0 +1 @@ +export { default } from './ResponseTimeDistribution' \ 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 index 0ba6ec5e8..ee227854b 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx @@ -35,7 +35,7 @@ function SessionsImpactedBySlowRequests(props: Props) { {...Styles.yaxis} allowDecimals={false} tickFormatter={val => Styles.tickFormatter(val)} - label={{ ...Styles.axisLabelLeft, value: "Number of Requests" }} + label={{ ...Styles.axisLabelLeft, value: "Number of Sessions" }} /> { - // if (map) { - // map.updateChoropleth(getSeries(metric.data.chart), { reset: true}); - // } - // }, []) + useEffect(() => { + if (map && map.updateChoropleth) { + const series = getSeries(metric.data.chart); + // console.log('series', series) + // map.updateChoropleth(series, {reset: true}); + } + }, []) const getDataset = () => { const { metric } = props; diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 6cd0c19e1..72de0ea3d 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -62,7 +62,12 @@ function DashboardSideMenu(props: Props) { {item.isPublic &&
} {item.isPinned &&
} {!item.isPinned && ( - +
togglePinned(item)} diff --git a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx index e61510760..c4aa215d7 100644 --- a/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetPredefinedChart/WidgetPredefinedChart.tsx @@ -26,6 +26,7 @@ import SessionsPerBrowser from 'App/components/Dashboard/Widgets/PredefinedWidge import CallWithErrors from '../../Widgets/PredefinedWidgets/CallWithErrors'; import SpeedIndexByLocation from '../../Widgets/PredefinedWidgets/SpeedIndexByLocation'; import SlowestResources from '../../Widgets/PredefinedWidgets/SlowestResources'; +import ResponseTimeDistribution from '../../Widgets/PredefinedWidgets/ResponseTimeDistribution'; interface Props { data: any; @@ -54,8 +55,10 @@ function WidgetPredefinedChart(props: Props) { return // PERFORMANCE - // case 'impacted_sessions_by_slow_pages': - // case 'pages_response_time_distribution': + case 'impacted_sessions_by_slow_pages': + return + case 'pages_response_time_distribution': + return case 'speed_location': return case 'cpu': diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx index 3b76f36de..4cb9e17cf 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx @@ -5,13 +5,9 @@ function TemplateOverlay() { return (
diff --git a/frontend/app/components/ui/ItemMenu/ItemMenu.js b/frontend/app/components/ui/ItemMenu/ItemMenu.js index 841d9e4c9..e97f6999b 100644 --- a/frontend/app/components/ui/ItemMenu/ItemMenu.js +++ b/frontend/app/components/ui/ItemMenu/ItemMenu.js @@ -46,28 +46,27 @@ export default class ItemMenu extends React.PureComponent { { items.filter(({ hidden }) => !hidden).map(({ onClick, text, icon, disabled = false, disabledMessage = '' }) => (
{} } role="menuitem" tabIndex="-1" > - -
- { icon && ( -
- -
- )} -
{ text }
-
-
+ +
+ { icon && ( +
+ +
+ )} +
{ text }
+
+
))}
diff --git a/frontend/app/components/ui/Loader/Loader.js b/frontend/app/components/ui/Loader/Loader.js index 5977af141..e286cd04a 100644 --- a/frontend/app/components/ui/Loader/Loader.js +++ b/frontend/app/components/ui/Loader/Loader.js @@ -1,7 +1,7 @@ import cn from 'classnames'; import styles from './loader.css'; -const Loader = React.memo(({ className, loading = true, children = null, size, style = { minHeight: '150px' } }) => (!loading ? children : +const Loader = React.memo(({ className = '', loading = true, children = null, size, style = { minHeight: '150px' } }) => (!loading ? children :
diff --git a/frontend/app/constants/countries.js b/frontend/app/constants/countries.js index bf3fbaea5..bab3836ae 100644 --- a/frontend/app/constants/countries.js +++ b/frontend/app/constants/countries.js @@ -1,4 +1,4 @@ -export const threeLetter = {'BD':'BGD', 'BE':'BEL', 'BF':'BFA', 'BG':'BGR', 'BA':'BIH', 'BB':'BRB', 'WF':'WLF', 'BL':'BLM', 'BM':'BMU', 'BN':'BRN', 'BO':'BOL', 'BH':'BHR', 'BI':'BDI', 'BJ':'BEN', 'BT':'BTN', 'JM':'JAM', 'BV':'BVT', 'BW':'BWA', 'WS':'WSM', 'BQ':'BES', 'BR':'BRA', 'BS':'BHS', 'JE':'JEY', 'BY':'BLR', 'BZ':'BLZ', 'RU':'RUS', 'RW':'RWA', 'RS':'SRB', 'TL':'TLS', 'RE':'REU', 'TM':'TKM', 'TJ':'TJK', 'RO':'ROU', 'TK':'TKL', 'GW':'GNB', 'GU':'GUM', 'GT':'GTM', 'GS':'SGS', 'GR':'GRC', 'GQ':'GNQ', 'GP':'GLP', 'JP':'JPN', 'GY':'GUY', 'GG':'GGY', 'GF':'GUF', 'GE':'GEO', 'GD':'GRD', 'GB':'GBR', 'GA':'GAB', 'SV':'SLV', 'GN':'GIN', 'GM':'GMB', 'GL':'GRL', 'GI':'GIB', 'GH':'GHA', 'OM':'OMN', 'TN':'TUN', 'JO':'JOR', 'HR':'HRV', 'HT':'HTI', 'HU':'HUN', 'HK':'HKG', 'HN':'HND', 'HM':'HMD', 'VE':'VEN', 'PR':'PRI', 'PS':'PSE', 'PW':'PLW', 'PT':'PRT', 'SJ':'SJM', 'PY':'PRY', 'IQ':'IRQ', 'PA':'PAN', 'PF':'PYF', 'PG':'PNG', 'PE':'PER', 'PK':'PAK', 'PH':'PHL', 'PN':'PCN', 'PL':'POL', 'PM':'SPM', 'ZM':'ZMB', 'EH':'ESH', 'EE':'EST', 'EG':'EGY', 'ZA':'ZAF', 'EC':'ECU', 'IT':'ITA', 'VN':'VNM', 'SB':'SLB', 'ET':'ETH', 'SO':'SOM', 'ZW':'ZWE', 'SA':'SAU', 'ES':'ESP', 'ER':'ERI', 'ME':'MNE', 'MD':'MDA', 'MG':'MDG', 'MF':'MAF', 'MA':'MAR', 'MC':'MCO', 'UZ':'UZB', 'MM':'MMR', 'ML':'MLI', 'MO':'MAC', 'MN':'MNG', 'MH':'MHL', 'MK':'MKD', 'MU':'MUS', 'MT':'MLT', 'MW':'MWI', 'MV':'MDV', 'MQ':'MTQ', 'MP':'MNP', 'MS':'MSR', 'OR':'SEÑMRT', 'IM':'IMN', 'UG':'UGA', 'TZ':'TZA', 'MIS':'MYS', 'MX':'MEX', 'IL':'ISR', 'FR':'FRA', 'IO':'IOT', 'SH':'SHN', 'FI':'FIN', 'FJ':'FJI', 'FK':'FLK', 'FM':'FSM', 'FO':'FRO', 'NI':'NIC', 'NL':'NLD', 'NO':'NOR', 'NA':'NAM', 'VU':'VUT', 'NC':'NCL', 'NE':'NER', 'NF':'NFK', 'NG':'NGA', 'NZ':'NZL', 'NP':'NPL', 'NR':'NRU', 'NU':'NIU', 'CK':'COK', 'XK':'XKX', 'CI':'CIV', 'CH':'CHE', 'CO':'COL', 'CN':'CHN', 'CM':'CMR', 'CL':'CHL', 'CC':'CCK', 'CA':'CAN', 'CG':'COG', 'CF':'CAF', 'CD':'COD', 'CZ':'CZE', 'CY':'CYP', 'CX':'CXR', 'CR':'CRI', 'CW':'CUW', 'CV':'CPV', 'CU':'CUB', 'SZ':'SWZ', 'SY':'SYR', 'SX':'SXM', 'KG':'KGZ', 'KE':'KEN', 'SS':'SSD', 'SR':'SUR', 'KI':'KIR', 'KH':'KHM', 'KN':'KNA', 'KM':'COM', 'ST':'STP', 'SK':'SVK', 'KR':'KOR', 'SI':'SVN', 'KP':'PRK', 'KW':'KWT', 'SN':'SEN', 'SM':'SMR', 'SL':'SLE', 'SC':'SYC', 'KZ':'KAZ', 'KY':'CYM', 'SG':'SGP', 'SE':'SWE', 'SD':'SDN', 'DO':'DOM', 'DM':'DMA', 'DJ':'DJI', 'DK':'DNK', 'VG':'VGB', 'DE':'DEU', 'YE':'YEM', 'DZ':'DZA', 'US':'USA', 'UY':'URY', 'YT':'MYT', 'UM':'UMI', 'LB':'LBN', 'LC':'LCA', 'LA':'LAO', 'TV':'TUV', 'TW':'TWN', 'TT':'TTO', 'TR':'TUR', 'LK':'LKA', 'LI':'LIE', 'LV':'LVA', 'TO':'TON', 'LT':'LTU', 'LU':'LUX', 'LR':'LBR', 'LS':'LSO', 'TH':'THA', 'TF':'ATF', 'TG':'TGO', 'TD':'TCD', 'TC':'TCA', 'LY':'LBY', 'VA':'VAT', 'VC':'VCT', 'AE':'ARE', 'AD':'AND', 'AG':'ATG', 'AF':'AFG', 'AI':'AIA', 'VI':'VIR', 'IS':'ISL', 'IR':'IRN', 'AM':'ARM', 'AL':'ALB', 'AO':'AGO', 'AQ':'ATA', 'AS':'ASM', 'AR':'ARG', 'AU':'AUS', 'AT':'AUT', 'AW':'ABW', 'IN':'IND', 'AX':'ALA', 'AZ':'AZE', 'IE':'IRL', 'ID':'IDN', 'UA':'UKR', 'QA':'QAT', 'MZ':'MOZ'} +export const threeLetter = {UN: 'BGD', BD: 'BGD', BE: 'BEL', BF: 'BFA', BG: 'BGR', BA: 'BIH', BB: 'BRB', WF: 'WLF', BL: 'BLM', BM: 'BMU', BN: 'BRN', BO: 'BOL', BH: 'BHR', BI: 'BDI', BJ: 'BEN', BT: 'BTN', JM: 'JAM', BV: 'BVT', BW: 'BWA', WS: 'WSM', BQ: 'BES', BR: 'BRA', BS: 'BHS', JE: 'JEY', BY: 'BLR', BZ: 'BLZ', RU: 'RUS', RW: 'RWA', RS: 'SRB', TL: 'TLS', RE: 'REU', TM: 'TKM', TJ: 'TJK', RO: 'ROU', TK: 'TKL', GW: 'GNB', GU: 'GUM', GT: 'GTM', GS: 'SGS', GR: 'GRC', GQ: 'GNQ', GP: 'GLP', JP: 'JPN', GY: 'GUY', GG: 'GGY', GF: 'GUF', GE: 'GEO', GD: 'GRD', GB: 'GBR', GA: 'GAB', SV: 'SLV', GN: 'GIN', GM: 'GMB', GL: 'GRL', GI: 'GIB', GH: 'GHA', OM: 'OMN', TN: 'TUN', JO: 'JOR', HR: 'HRV', HT: 'HTI', HU: 'HUN', HK: 'HKG', HN: 'HND', HM: 'HMD', VE: 'VEN', PR: 'PRI', PS: 'PSE', PW: 'PLW', PT: 'PRT', SJ: 'SJM', PY: 'PRY', IQ: 'IRQ', PA: 'PAN', PF: 'PYF', PG: 'PNG', PE: 'PER', PK: 'PAK', PH: 'PHL', PN: 'PCN', PL: 'POL', PM: 'SPM', ZM: 'ZMB', EH: 'ESH', EE: 'EST', EG: 'EGY', ZA: 'ZAF', EC: 'ECU', IT: 'ITA', VN: 'VNM', SB: 'SLB', ET: 'ETH', SO: 'SOM', ZW: 'ZWE', SA: 'SAU', ES: 'ESP', ER: 'ERI', ME: 'MNE', MD: 'MDA', MG: 'MDG', MF: 'MAF', MA: 'MAR', MC: 'MCO', UZ: 'UZB', MM: 'MMR', ML: 'MLI', MO: 'MAC', MN: 'MNG', MH: 'MHL', MK: 'MKD', MU: 'MUS', MT: 'MLT', MW: 'MWI', MV: 'MDV', MQ: 'MTQ', MP: 'MNP', MS: 'MSR', OR: 'SEÑ', IM: 'IMN', UG: 'UGA', TZ: 'TZA', IS: 'MYS', MX: 'MEX', IL: 'ISR', FR: 'FRA', IO: 'IOT', SH: 'SHN', FI: 'FIN', FJ: 'FJI', FK: 'FLK', FM: 'FSM', FO: 'FRO', NI: 'NIC', NL: 'NLD', NO: 'NOR', NA: 'NAM', VU: 'VUT', NC: 'NCL', NE: 'NER', NF: 'NFK', NG: 'NGA', NZ: 'NZL', NP: 'NPL', NR: 'NRU', NU: 'NIU', CK: 'COK', XK: 'XKX', CI: 'CIV', CH: 'CHE', CO: 'COL', CN: 'CHN', CM: 'CMR', CL: 'CHL', CC: 'CCK', CA: 'CAN', CG: 'COG', CF: 'CAF', CD: 'COD', CZ: 'CZE', CY: 'CYP', CX: 'CXR', CR: 'CRI', CW: 'CUW', CV: 'CPV', CU: 'CUB', SZ: 'SWZ', SY: 'SYR', SX: 'SXM', KG: 'KGZ', KE: 'KEN', SS: 'SSD', SR: 'SUR', KI: 'KIR', KH: 'KHM', KN: 'KNA', KM: 'COM', ST: 'STP', SK: 'SVK', KR: 'KOR', SI: 'SVN', KP: 'PRK', KW: 'KWT', SN: 'SEN', SM: 'SMR', SL: 'SLE', SC: 'SYC', KZ: 'KAZ', KY: 'CYM', SG: 'SGP', SE: 'SWE', SD: 'SDN', DO: 'DOM', DM: 'DMA', DJ: 'DJI', DK: 'DNK', VG: 'VGB', DE: 'DEU', YE: 'YEM', DZ: 'DZA', US: 'USA', UY: 'URY', YT: 'MYT', UM: 'UMI', LB: 'LBN', LC: 'LCA', LA: 'LAO', TV: 'TUV', TW: 'TWN', TT: 'TTO', TR: 'TUR', LK: 'LKA', LI: 'LIE', LV: 'LVA', TO: 'TON', LT: 'LTU', LU: 'LUX', LR: 'LBR', LS: 'LSO', TH: 'THA', TF: 'ATF', TG: 'TGO', TD: 'TCD', TC: 'TCA', LY: 'LBY', VA: 'VAT', VC: 'VCT', AE: 'ARE', AD: 'AND', AG: 'ATG', AF: 'AFG', AI: 'AIA', VI: 'VIR', IS: 'ISL', IR: 'IRN', AM: 'ARM', AL: 'ALB', AO: 'AGO', AQ: 'ATA', AS: 'ASM', AR: 'ARG', AU: 'AUS', AT: 'AUT', AW: 'ABW', IN: 'IND', AX: 'ALA', AZ: 'AZE', IE: 'IRL', ID: 'IDN', UA: 'UKR', QA: 'QAT', MZ: 'MOZ'} export default { AC: 'Ascension Island', AD: 'Andorra', diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index 4d20ca0f1..0a465f5cf 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -72,7 +72,9 @@ export default class Widget implements IWidget { position: number = 0 data: any = { chart: [], - namesMap: {} + namesMap: {}, + avg: 0, + percentiles: [], } isLoading: boolean = false isValid: boolean = false From eb4aa50211e2fcc3976f0538d9249a8228f6b59c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 12:35:29 +0200 Subject: [PATCH 186/294] feat(ui) - dashboard - other widgets --- .../SlowestResources/SlowestResources.tsx | 4 +- ...tion.js => SpeedIndexByLocation copy.js__} | 0 .../SpeedIndexByLocation.css | 21 ++++ .../SpeedIndexByLocation.tsx | 102 ++++++++++++++++++ frontend/app/constants/countries.js | 2 +- frontend/app/styles/main.css | 7 ++ 6 files changed, 133 insertions(+), 3 deletions(-) rename frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/{SpeedIndexByLocation.js => SpeedIndexByLocation copy.js__} (100%) create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.css create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.tsx diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx index ca62855b0..c4bbb1ed9 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SlowestResources/SlowestResources.tsx @@ -57,7 +57,7 @@ interface Props { data: any metric?: any } -function MissingResources(props: Props) { +function SlowestResources(props: Props) { const { data, metric } = props; return ( @@ -78,4 +78,4 @@ function MissingResources(props: Props) { ); } -export default MissingResources; \ No newline at end of file +export default SlowestResources; \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation copy.js__ similarity index 100% rename from frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.js rename to frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation copy.js__ diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.css new file mode 100644 index 000000000..ab474e37b --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.css @@ -0,0 +1,21 @@ +.maps { + height: auto; + width: 110%; + stroke: $gray-medium; + stroke-width: 1; + stroke-linecap: round; + stroke-linejoin: round; + margin-top: -20px; + +} + +.location { + fill: $gray-light !important; + cursor: pointer; + + &:focus, + &:hover { + fill: #b8e2b3; + outline: 0; + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.tsx new file mode 100644 index 000000000..788e4f020 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.tsx @@ -0,0 +1,102 @@ +import React, { useEffect } from 'react'; +import { NoContent } from 'UI'; +import { Styles, AvgLabel } from '../../common'; +import Scale from './Scale'; +import { threeLetter } from 'App/constants/countries'; +import { colorScale } from 'App/utils'; +import { observer } from 'mobx-react-lite'; +import { numberWithCommas } from 'App/utils'; +import WorldMap from "@svg-maps/world"; +import { SVGMap } from "react-svg-map"; +import "react-svg-map/lib/index.css"; +import stl from './SpeedIndexByLocation.css'; + +interface Props { + metric?: any +} +function SpeedIndexByLocation(props: Props) { + const { metric } = props; + const wrapper: any = React.useRef(null); + let map: any = null; + + const getSeries = data => { + const series: any[] = []; + data.forEach(item => { + const d = [threeLetter[item.userCountry], Math.round(item.avg)] + series.push(d) + }) + + return series; + } + + useEffect(() => { + // if (!wrapper && !wrapper.current) return + + }, []) + + // useEffect(() => { + // if (map && map.updateChoropleth) { + // const series = getSeries(metric.data.chart); + // // console.log('series', series) + // // map.updateChoropleth(series, {reset: true}); + // } + // }, []) + + const getDataset = () => { + const { metric } = props; + const colors = Styles.colors; + + var dataset = {}; + const series = getSeries(metric.data.chart); + var onlyValues = series.map(function(obj){ return obj[1]; }); + const paletteScale = colorScale(onlyValues, [...colors].reverse()); + + // fill dataset in appropriate format + series.forEach(function(item){ + var iso = item[0], value = item[1]; + dataset[iso] = { numberOfThings: value, fillColor: paletteScale(value) }; + }); + return dataset; + } + + const getLocationClassName = (location, index) => { + // Generate random heat map + return `svg-map__location svg-map__location--heat${index % 4}`; + } + + return ( + +
+ +
+ +
+
+ console.log(e.target)} + /> +
+ {/*
+ {this.state.pointedLocation} +
*/} +
+ ); +} + +export default observer(SpeedIndexByLocation); \ No newline at end of file diff --git a/frontend/app/constants/countries.js b/frontend/app/constants/countries.js index bab3836ae..68a65b000 100644 --- a/frontend/app/constants/countries.js +++ b/frontend/app/constants/countries.js @@ -1,4 +1,4 @@ -export const threeLetter = {UN: 'BGD', BD: 'BGD', BE: 'BEL', BF: 'BFA', BG: 'BGR', BA: 'BIH', BB: 'BRB', WF: 'WLF', BL: 'BLM', BM: 'BMU', BN: 'BRN', BO: 'BOL', BH: 'BHR', BI: 'BDI', BJ: 'BEN', BT: 'BTN', JM: 'JAM', BV: 'BVT', BW: 'BWA', WS: 'WSM', BQ: 'BES', BR: 'BRA', BS: 'BHS', JE: 'JEY', BY: 'BLR', BZ: 'BLZ', RU: 'RUS', RW: 'RWA', RS: 'SRB', TL: 'TLS', RE: 'REU', TM: 'TKM', TJ: 'TJK', RO: 'ROU', TK: 'TKL', GW: 'GNB', GU: 'GUM', GT: 'GTM', GS: 'SGS', GR: 'GRC', GQ: 'GNQ', GP: 'GLP', JP: 'JPN', GY: 'GUY', GG: 'GGY', GF: 'GUF', GE: 'GEO', GD: 'GRD', GB: 'GBR', GA: 'GAB', SV: 'SLV', GN: 'GIN', GM: 'GMB', GL: 'GRL', GI: 'GIB', GH: 'GHA', OM: 'OMN', TN: 'TUN', JO: 'JOR', HR: 'HRV', HT: 'HTI', HU: 'HUN', HK: 'HKG', HN: 'HND', HM: 'HMD', VE: 'VEN', PR: 'PRI', PS: 'PSE', PW: 'PLW', PT: 'PRT', SJ: 'SJM', PY: 'PRY', IQ: 'IRQ', PA: 'PAN', PF: 'PYF', PG: 'PNG', PE: 'PER', PK: 'PAK', PH: 'PHL', PN: 'PCN', PL: 'POL', PM: 'SPM', ZM: 'ZMB', EH: 'ESH', EE: 'EST', EG: 'EGY', ZA: 'ZAF', EC: 'ECU', IT: 'ITA', VN: 'VNM', SB: 'SLB', ET: 'ETH', SO: 'SOM', ZW: 'ZWE', SA: 'SAU', ES: 'ESP', ER: 'ERI', ME: 'MNE', MD: 'MDA', MG: 'MDG', MF: 'MAF', MA: 'MAR', MC: 'MCO', UZ: 'UZB', MM: 'MMR', ML: 'MLI', MO: 'MAC', MN: 'MNG', MH: 'MHL', MK: 'MKD', MU: 'MUS', MT: 'MLT', MW: 'MWI', MV: 'MDV', MQ: 'MTQ', MP: 'MNP', MS: 'MSR', OR: 'SEÑ', IM: 'IMN', UG: 'UGA', TZ: 'TZA', IS: 'MYS', MX: 'MEX', IL: 'ISR', FR: 'FRA', IO: 'IOT', SH: 'SHN', FI: 'FIN', FJ: 'FJI', FK: 'FLK', FM: 'FSM', FO: 'FRO', NI: 'NIC', NL: 'NLD', NO: 'NOR', NA: 'NAM', VU: 'VUT', NC: 'NCL', NE: 'NER', NF: 'NFK', NG: 'NGA', NZ: 'NZL', NP: 'NPL', NR: 'NRU', NU: 'NIU', CK: 'COK', XK: 'XKX', CI: 'CIV', CH: 'CHE', CO: 'COL', CN: 'CHN', CM: 'CMR', CL: 'CHL', CC: 'CCK', CA: 'CAN', CG: 'COG', CF: 'CAF', CD: 'COD', CZ: 'CZE', CY: 'CYP', CX: 'CXR', CR: 'CRI', CW: 'CUW', CV: 'CPV', CU: 'CUB', SZ: 'SWZ', SY: 'SYR', SX: 'SXM', KG: 'KGZ', KE: 'KEN', SS: 'SSD', SR: 'SUR', KI: 'KIR', KH: 'KHM', KN: 'KNA', KM: 'COM', ST: 'STP', SK: 'SVK', KR: 'KOR', SI: 'SVN', KP: 'PRK', KW: 'KWT', SN: 'SEN', SM: 'SMR', SL: 'SLE', SC: 'SYC', KZ: 'KAZ', KY: 'CYM', SG: 'SGP', SE: 'SWE', SD: 'SDN', DO: 'DOM', DM: 'DMA', DJ: 'DJI', DK: 'DNK', VG: 'VGB', DE: 'DEU', YE: 'YEM', DZ: 'DZA', US: 'USA', UY: 'URY', YT: 'MYT', UM: 'UMI', LB: 'LBN', LC: 'LCA', LA: 'LAO', TV: 'TUV', TW: 'TWN', TT: 'TTO', TR: 'TUR', LK: 'LKA', LI: 'LIE', LV: 'LVA', TO: 'TON', LT: 'LTU', LU: 'LUX', LR: 'LBR', LS: 'LSO', TH: 'THA', TF: 'ATF', TG: 'TGO', TD: 'TCD', TC: 'TCA', LY: 'LBY', VA: 'VAT', VC: 'VCT', AE: 'ARE', AD: 'AND', AG: 'ATG', AF: 'AFG', AI: 'AIA', VI: 'VIR', IS: 'ISL', IR: 'IRN', AM: 'ARM', AL: 'ALB', AO: 'AGO', AQ: 'ATA', AS: 'ASM', AR: 'ARG', AU: 'AUS', AT: 'AUT', AW: 'ABW', IN: 'IND', AX: 'ALA', AZ: 'AZE', IE: 'IRL', ID: 'IDN', UA: 'UKR', QA: 'QAT', MZ: 'MOZ'} +export const threeLetter = {BD: 'BGD', BE: 'BEL', BF: 'BFA', BG: 'BGR', BA: 'BIH', BB: 'BRB', WF: 'WLF', BL: 'BLM', BM: 'BMU', BN: 'BRN', BO: 'BOL', BH: 'BHR', BI: 'BDI', BJ: 'BEN', BT: 'BTN', JM: 'JAM', BV: 'BVT', BW: 'BWA', WS: 'WSM', BQ: 'BES', BR: 'BRA', BS: 'BHS', JE: 'JEY', BY: 'BLR', BZ: 'BLZ', RU: 'RUS', RW: 'RWA', RS: 'SRB', TL: 'TLS', RE: 'REU', TM: 'TKM', TJ: 'TJK', RO: 'ROU', TK: 'TKL', GW: 'GNB', GU: 'GUM', GT: 'GTM', GS: 'SGS', GR: 'GRC', GQ: 'GNQ', GP: 'GLP', JP: 'JPN', GY: 'GUY', GG: 'GGY', GF: 'GUF', GE: 'GEO', GD: 'GRD', GB: 'GBR', GA: 'GAB', SV: 'SLV', GN: 'GIN', GM: 'GMB', GL: 'GRL', GI: 'GIB', GH: 'GHA', OM: 'OMN', TN: 'TUN', JO: 'JOR', HR: 'HRV', HT: 'HTI', HU: 'HUN', HK: 'HKG', HN: 'HND', HM: 'HMD', VE: 'VEN', PR: 'PRI', PS: 'PSE', PW: 'PLW', PT: 'PRT', SJ: 'SJM', PY: 'PRY', IQ: 'IRQ', PA: 'PAN', PF: 'PYF', PG: 'PNG', PE: 'PER', PK: 'PAK', PH: 'PHL', PN: 'PCN', PL: 'POL', PM: 'SPM', ZM: 'ZMB', EH: 'ESH', EE: 'EST', EG: 'EGY', ZA: 'ZAF', EC: 'ECU', IT: 'ITA', VN: 'VNM', SB: 'SLB', ET: 'ETH', SO: 'SOM', ZW: 'ZWE', SA: 'SAU', ES: 'ESP', ER: 'ERI', ME: 'MNE', MD: 'MDA', MG: 'MDG', MF: 'MAF', MA: 'MAR', MC: 'MCO', UZ: 'UZB', MM: 'MMR', ML: 'MLI', MO: 'MAC', MN: 'MNG', MH: 'MHL', MK: 'MKD', MU: 'MUS', MT: 'MLT', MW: 'MWI', MV: 'MDV', MQ: 'MTQ', MP: 'MNP', MS: 'MSR', OR: 'SEÑ', IM: 'IMN', UG: 'UGA', TZ: 'TZA', IS: 'MYS', MX: 'MEX', IL: 'ISR', FR: 'FRA', IO: 'IOT', SH: 'SHN', FI: 'FIN', FJ: 'FJI', FK: 'FLK', FM: 'FSM', FO: 'FRO', NI: 'NIC', NL: 'NLD', NO: 'NOR', NA: 'NAM', VU: 'VUT', NC: 'NCL', NE: 'NER', NF: 'NFK', NG: 'NGA', NZ: 'NZL', NP: 'NPL', NR: 'NRU', NU: 'NIU', CK: 'COK', XK: 'XKX', CI: 'CIV', CH: 'CHE', CO: 'COL', CN: 'CHN', CM: 'CMR', CL: 'CHL', CC: 'CCK', CA: 'CAN', CG: 'COG', CF: 'CAF', CD: 'COD', CZ: 'CZE', CY: 'CYP', CX: 'CXR', CR: 'CRI', CW: 'CUW', CV: 'CPV', CU: 'CUB', SZ: 'SWZ', SY: 'SYR', SX: 'SXM', KG: 'KGZ', KE: 'KEN', SS: 'SSD', SR: 'SUR', KI: 'KIR', KH: 'KHM', KN: 'KNA', KM: 'COM', ST: 'STP', SK: 'SVK', KR: 'KOR', SI: 'SVN', KP: 'PRK', KW: 'KWT', SN: 'SEN', SM: 'SMR', SL: 'SLE', SC: 'SYC', KZ: 'KAZ', KY: 'CYM', SG: 'SGP', SE: 'SWE', SD: 'SDN', DO: 'DOM', DM: 'DMA', DJ: 'DJI', DK: 'DNK', VG: 'VGB', DE: 'DEU', YE: 'YEM', DZ: 'DZA', US: 'USA', UY: 'URY', YT: 'MYT', UM: 'UMI', LB: 'LBN', LC: 'LCA', LA: 'LAO', TV: 'TUV', TW: 'TWN', TT: 'TTO', TR: 'TUR', LK: 'LKA', LI: 'LIE', LV: 'LVA', TO: 'TON', LT: 'LTU', LU: 'LUX', LR: 'LBR', LS: 'LSO', TH: 'THA', TF: 'ATF', TG: 'TGO', TD: 'TCD', TC: 'TCA', LY: 'LBY', VA: 'VAT', VC: 'VCT', AE: 'ARE', AD: 'AND', AG: 'ATG', AF: 'AFG', AI: 'AIA', VI: 'VIR', IS: 'ISL', IR: 'IRN', AM: 'ARM', AL: 'ALB', AO: 'AGO', AQ: 'ATA', AS: 'ASM', AR: 'ARG', AU: 'AUS', AT: 'AUT', AW: 'ABW', IN: 'IND', AX: 'ALA', AZ: 'AZE', IE: 'IRL', ID: 'IDN', UA: 'UKR', QA: 'QAT', MZ: 'MOZ'} export default { AC: 'Ascension Island', AD: 'Andorra', diff --git a/frontend/app/styles/main.css b/frontend/app/styles/main.css index 3b7f5fe4b..d59334bd2 100644 --- a/frontend/app/styles/main.css +++ b/frontend/app/styles/main.css @@ -147,4 +147,11 @@ height: 100vh; overflow-y: hidden; padding-right: 15px; +} + +.svg-map { + &__location { + fill: #ddd !important; + cursor: pointer; + } } \ No newline at end of file From f7c06ff12536eb7ae270c5868f7974b490f5f8ef Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 13:03:06 +0200 Subject: [PATCH 187/294] feat(ui) - dashboard - filter fix duration --- .../FilterAutoCompleteLocal.tsx | 27 ++++++++++++------- .../Filters/FilterValue/FilterValue.tsx | 1 + frontend/app/types/filter/newFilter.js | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/frontend/app/components/shared/Filters/FilterAutoCompleteLocal/FilterAutoCompleteLocal.tsx b/frontend/app/components/shared/Filters/FilterAutoCompleteLocal/FilterAutoCompleteLocal.tsx index 542dfce1c..2030d422a 100644 --- a/frontend/app/components/shared/Filters/FilterAutoCompleteLocal/FilterAutoCompleteLocal.tsx +++ b/frontend/app/components/shared/Filters/FilterAutoCompleteLocal/FilterAutoCompleteLocal.tsx @@ -1,9 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { Icon, Loader } from 'UI'; -// import { debounce } from 'App/utils'; +import { Icon } from 'UI'; import stl from './FilterAutoCompleteLocal.css'; -// import cn from 'classnames'; - interface Props { showOrButton?: boolean; showCloseButton?: boolean; @@ -15,6 +12,7 @@ interface Props { icon?: string; type?: string; isMultilple?: boolean; + allowDecimals?: boolean; } function FilterAutoCompleteLocal(props: Props) { @@ -28,15 +26,24 @@ function FilterAutoCompleteLocal(props: Props) { icon = null, type = "text", isMultilple = true, + allowDecimals = true, } = props; const [showModal, setShowModal] = useState(true) const [query, setQuery] = useState(value); - // const debounceOnSelect = debounce(props.onSelect, 500); - const onInputChange = ({ target: { value } }) => { - setQuery(value); - props.onSelect(null, { value }); - } + const onInputChange = (e) => { + if(allowDecimals) { + const value = e.target.value; + setQuery(value); + props.onSelect(null, { value }); + } else { + const value = e.target.value.replace(/[^\d]/, ""); + if (+value !== 0) { + setQuery(value); + props.onSelect(null, { value }); + } + } + }; useEffect(() => { setQuery(value); @@ -58,7 +65,7 @@ function FilterAutoCompleteLocal(props: Props) {
setShowModal(true)} value={ query } diff --git a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx index 2fd2fc132..4e8d4001f 100644 --- a/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx +++ b/frontend/app/components/shared/Filters/FilterValue/FilterValue.tsx @@ -137,6 +137,7 @@ function FilterValue(props: Props) { onSelect={(e, item) => debounceOnSelect(e, item, valueIndex)} icon={filter.icon} type="number" + allowDecimals={false} isMultilple={false} /> // Date: Fri, 15 Apr 2022 13:03:25 +0200 Subject: [PATCH 188/294] feat(ui) - dashboard - filter fix duration --- frontend/package-lock.json | 133 ++++++++++++++++++++++++++----------- frontend/package.json | 2 + 2 files changed, 98 insertions(+), 37 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5393d1fcc..5fa927e01 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "1.3.6", "dependencies": { "@sentry/browser": "^5.21.1", + "@svg-maps/world": "^1.0.1", "classnames": "^2.2.6", "codemirror": "^5.62.3", "copy-to-clipboard": "^3.3.1", @@ -41,6 +42,7 @@ "react-redux": "^5.1.2", "react-router": "^4.3.1", "react-router-dom": "^4.3.1", + "react-svg-map": "^2.2.0", "react-tippy": "^1.4.0", "react-toastify": "^5.5.0", "react-virtualized": "^9.22.2", @@ -4544,6 +4546,11 @@ "react-dom": "^16.8.0 || ^17.0.0" } }, + "node_modules/@svg-maps/world": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@svg-maps/world/-/world-1.0.1.tgz", + "integrity": "sha512-Mawh/jEYBBHnug9S17PyePLYKJ+Xd0Bbh96mCePebpbvcbJu5YKpfKhpyMeLFmmdWPrSFxl0f0MTsJfXU0gSaQ==" + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -4650,7 +4657,7 @@ "version": "14.18.12", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", - "dev": true + "devOptional": true }, "node_modules/@types/node-fetch": { "version": "2.6.1", @@ -4702,7 +4709,7 @@ "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 + "devOptional": true }, "node_modules/@types/q": { "version": "1.5.5", @@ -4720,7 +4727,7 @@ "version": "17.0.43", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4740,13 +4747,13 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "dev": true + "devOptional": 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 + "devOptional": true }, "node_modules/@types/source-list-map": { "version": "0.1.2", @@ -19275,6 +19282,18 @@ "react-dom": ">=15.0.0" } }, + "node_modules/react-svg-map": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-svg-map/-/react-svg-map-2.2.0.tgz", + "integrity": "sha512-slZTt4ffXgOb3ND/WEHOc0ojX5lBEv9FKbTU3tWnTLZPQ9L0e686SBqgVxmsuTD+o52Aor87InMzAzDRTQedpA==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^16.0.0", + "react-dom": "^16.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", @@ -29303,6 +29322,11 @@ "store2": "^2.12.0" } }, + "@svg-maps/world": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@svg-maps/world/-/world-1.0.1.tgz", + "integrity": "sha512-Mawh/jEYBBHnug9S17PyePLYKJ+Xd0Bbh96mCePebpbvcbJu5YKpfKhpyMeLFmmdWPrSFxl0f0MTsJfXU0gSaQ==" + }, "@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -29406,7 +29430,7 @@ "version": "14.18.12", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", - "dev": true + "devOptional": true }, "@types/node-fetch": { "version": "2.6.1", @@ -29458,7 +29482,7 @@ "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 + "devOptional": true }, "@types/q": { "version": "1.5.5", @@ -29476,7 +29500,7 @@ "version": "17.0.43", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", - "dev": true, + "devOptional": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -29487,7 +29511,7 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "dev": true + "devOptional": true } } }, @@ -29504,7 +29528,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true + "devOptional": true }, "@types/source-list-map": { "version": "0.1.2", @@ -29869,13 +29893,15 @@ "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 + "dev": true, + "requires": {} }, "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 + "dev": true, + "requires": {} }, "ansi-align": { "version": "3.0.1", @@ -30503,7 +30529,8 @@ "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 + "dev": true, + "requires": {} }, "babel-plugin-polyfill-corejs2": { "version": "0.3.1", @@ -31380,7 +31407,8 @@ "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 + "dev": true, + "requires": {} }, "class-utils": { "version": "0.3.6", @@ -32442,7 +32470,8 @@ "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 + "dev": true, + "requires": {} }, "css-loader": { "version": "3.6.0", @@ -32753,7 +32782,8 @@ "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 + "dev": true, + "requires": {} }, "csso": { "version": "4.2.0", @@ -33626,7 +33656,8 @@ "ws": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} } } }, @@ -37422,7 +37453,8 @@ "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 + "dev": true, + "requires": {} }, "math-expression-evaluator": { "version": "1.3.14", @@ -37898,7 +37930,8 @@ "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==" + "integrity": "sha512-U/kMSFtV/bNVgY01FuiGWpRkaQVHozBq5CEBZltFvPt4FcV111hEWkgwqVg9GPPZSEuEdV438PEz8mk8mKpYlA==", + "requires": {} }, "moment": { "version": "2.29.1", @@ -39211,25 +39244,29 @@ "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 + "dev": true, + "requires": {} }, "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 + "dev": true, + "requires": {} }, "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 + "dev": true, + "requires": {} }, "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 + "dev": true, + "requires": {} }, "postcss-flexbugs-fixes": { "version": "4.2.1", @@ -39903,7 +39940,8 @@ "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 + "dev": true, + "requires": {} }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -40799,23 +40837,27 @@ "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==" + "integrity": "sha512-OfX0ThSxRYEVGaQSt0DlXfyl5w4DbXHsXetyeivmoQrh9xA9bzVPHNf8aAhOIiwiaxX2WYWpLDB3gcpsDJ9oww==", + "requires": {} }, "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==" + "integrity": "sha512-Cksbgbviuf2mJfMyrKmcu7ycK6zX/ukuQO8dvRZdFWqATf5joalhjFc6etnBdGCcPA2LbhIwz+OPnQxLN/j1Fw==", + "requires": {} }, "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 + "dev": true, + "requires": {} }, "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==" + "integrity": "sha512-96qA+mbZyBRmh/3Y5aDgrYLwLndbaRjkP3GlXQtPEQbIH0P66xGcHJ7ui6y/MN85AZWq/V3drA1fJOiEcVkAVA==", + "requires": {} }, "react-datepicker": { "version": "2.16.0", @@ -40908,7 +40950,8 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", - "dev": true + "dev": true, + "requires": {} }, "react-dom": { "version": "16.14.0", @@ -40986,7 +41029,8 @@ "react-lazyload": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-lazyload/-/react-lazyload-3.2.0.tgz", - "integrity": "sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw==" + "integrity": "sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw==", + "requires": {} }, "react-lifecycles-compat": { "version": "3.0.4", @@ -40996,7 +41040,8 @@ "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==" + "integrity": "sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q==", + "requires": {} }, "react-popper": { "version": "1.3.11", @@ -41192,6 +41237,14 @@ } } }, + "react-svg-map": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-svg-map/-/react-svg-map-2.2.0.tgz", + "integrity": "sha512-slZTt4ffXgOb3ND/WEHOc0ojX5lBEv9FKbTU3tWnTLZPQ9L0e686SBqgVxmsuTD+o52Aor87InMzAzDRTQedpA==", + "requires": { + "prop-types": "^15.7.2" + } + }, "react-syntax-highlighter": { "version": "13.5.3", "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-13.5.3.tgz", @@ -41438,12 +41491,14 @@ "redux-immutable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz", - "integrity": "sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=" + "integrity": "sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=", + "requires": {} }, "redux-thunk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", - "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" + "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", + "requires": {} }, "refractor": { "version": "3.6.0", @@ -44624,12 +44679,14 @@ "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==" + "integrity": "sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw==", + "requires": {} }, "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==" + "integrity": "sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==", + "requires": {} }, "use-latest": { "version": "1.2.0", @@ -46199,7 +46256,8 @@ "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 + "dev": true, + "requires": {} }, "webpack-hot-middleware": { "version": "2.25.1", @@ -46515,7 +46573,8 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true + "dev": true, + "requires": {} }, "xml": { "version": "1.0.1", diff --git a/frontend/package.json b/frontend/package.json index 5806baf98..a613d5919 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@sentry/browser": "^5.21.1", + "@svg-maps/world": "^1.0.1", "classnames": "^2.2.6", "codemirror": "^5.62.3", "copy-to-clipboard": "^3.3.1", @@ -48,6 +49,7 @@ "react-redux": "^5.1.2", "react-router": "^4.3.1", "react-router-dom": "^4.3.1", + "react-svg-map": "^2.2.0", "react-tippy": "^1.4.0", "react-toastify": "^5.5.0", "react-virtualized": "^9.22.2", From 8756fd16dac571b643fab8e655c7d6b89dbf513a Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 13:32:38 +0200 Subject: [PATCH 189/294] feat(ui) - widget drilldown on table nad pie chart --- .../CustomMetricTable/CustomMetricTable.tsx | 1 + .../components/WidgetChart/WidgetChart.tsx | 42 +++++++++++-------- .../WidgetSessions/WidgetSessions.tsx | 2 +- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx index a43a35fc8..f83f48466 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx @@ -33,6 +33,7 @@ function CustomMetriTable(props: Props) { const rows = List(data.values); const onClickHandler = (event, data) => { + console.log('onClickHandler', data); const filters = Array(); let filter = { ...filtersMap[metric.metricOf] } filter.value = [data.name] diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index c3f8d10c2..0f073e8e3 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -28,26 +28,28 @@ function WidgetChart(props: Props) { const prevMetricRef = useRef(); const [data, setData] = useState(metric.data); + const isTableWidget = metric.metricType === 'table' && metric.viewType === 'table'; + const isPieChart = metric.metricType === 'table' && metric.viewType === 'pieChart'; + const onChartClick = (event: any) => { if (event) { - const payload = event.activePayload[0].payload; - const timestamp = payload.timestamp; - const periodTimestamps = metric.metricType === 'timeseries' ? - getStartAndEndTimestampsByDensity(timestamp, period.start, period.end, params.density) : - period.toTimestamps(); + if (isTableWidget || isPieChart) { + const periodTimestamps = period.toTimestamps() + drillDownFilter.merge({ + filters: event, + startTimestamp: periodTimestamps.startTimestamp, + endTimestamp: periodTimestamps.endTimestamp, + }); + } else { + const payload = event.activePayload[0].payload; + const timestamp = payload.timestamp; + const periodTimestamps = getStartAndEndTimestampsByDensity(timestamp, period.start, period.end, params.density); - drillDownFilter.merge({ - startTimestamp: periodTimestamps.startTimestamp, - endTimestamp: periodTimestamps.endTimestamp, - }); - - // const activeWidget = { - // widget: metric, - // period: period, - // ...periodTimestamps, - // timestamp: payload.timestamp, - // index, - // } + drillDownFilter.merge({ + startTimestamp: periodTimestamps.startTimestamp, + endTimestamp: periodTimestamps.endTimestamp, + }); + } } } @@ -100,7 +102,10 @@ function WidgetChart(props: Props) { if (metricType === 'table') { if (viewType === 'table') { - return ; + return ; } else if (viewType === 'pieChart') { return ( ) } diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index e6c9cb808..54982ba9c 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -42,7 +42,7 @@ function WidgetSessions(props: Props) { console.log('res', res) setData(res); }); - }, [filter.startTimestamp, filter.endTimestamp, widget.filter]); + }, [filter.startTimestamp, filter.endTimestamp, filter.filters]); return useObserver(() => (
From b54f8d1de35545a3320b6aae19facec81aed259a Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 13:41:59 +0200 Subject: [PATCH 190/294] feat(ui) - check for key --- frontend/app/mstore/dashboardStore.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index ba8c23e58..503ea285f 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -358,7 +358,8 @@ export default class DashboardStore implements IDashboardSotre { const categories: any[] = [] response.forEach(category => { const widgets: any[] = [] - category.widgets.forEach(widget => { + // TODO speed_location is not supported yet + category.widgets.filter(w => w.predefinedKey !== 'speed_location').forEach(widget => { const w = new Widget().fromJson(widget) widgets.push(w) }) From 71623cf9925aa65ebe9b2c5c76f8a1c410870605 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 13:45:29 +0200 Subject: [PATCH 191/294] feat(api): dashboard support pagination for sessions drill down --- api/chalicelib/core/custom_metrics.py | 2 ++ api/chalicelib/core/funnels.py | 2 -- api/chalicelib/core/heatmaps.py | 1 - api/chalicelib/core/insights.py | 24 ++++++++++-------------- api/chalicelib/core/metadata.py | 1 - api/chalicelib/core/projects.py | 1 - api/chalicelib/core/significance.py | 14 +++++++------- api/schemas.py | 11 +++++++---- 8 files changed, 26 insertions(+), 30 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 7c6311b75..2ecc466c6 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -97,6 +97,8 @@ def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessi for s in metric.series: s.filter.startDate = data.startTimestamp s.filter.endDate = data.endTimestamp + s.filter.limit = data.limit + s.filter.page = data.page results.append({"seriesId": s.series_id, "seriesName": s.name, **sessions.search2_pg(data=s.filter, project_id=project_id, user_id=user_id)}) diff --git a/api/chalicelib/core/funnels.py b/api/chalicelib/core/funnels.py index 1dc9e3347..16e95989d 100644 --- a/api/chalicelib/core/funnels.py +++ b/api/chalicelib/core/funnels.py @@ -261,7 +261,6 @@ def get_issues(project_id, user_id, funnel_id, range_value=None, start_date=None }} -@dev.timed def get_issues_on_the_fly(funnel_id, user_id, project_id, data: schemas.FunnelSearchPayloadSchema): data.events = filter_stages(data.events) data.events = __fix_stages(data.events) @@ -313,7 +312,6 @@ def get(funnel_id, project_id, user_id, flatten=True, fix_stages=True): return f -@dev.timed def search_by_issue(user_id, project_id, funnel_id, issue_id, data: schemas.FunnelSearchPayloadSchema, range_value=None, start_date=None, end_date=None): if len(data.events) == 0: diff --git a/api/chalicelib/core/heatmaps.py b/api/chalicelib/core/heatmaps.py index eacd6cd86..5aacb1375 100644 --- a/api/chalicelib/core/heatmaps.py +++ b/api/chalicelib/core/heatmaps.py @@ -3,7 +3,6 @@ from chalicelib.utils import helper, pg_client from chalicelib.utils import dev -@dev.timed def get_by_url(project_id, data): args = {"startDate": data.get('startDate', TimeUTC.now(delta_days=-30)), "endDate": data.get('endDate', TimeUTC.now()), diff --git a/api/chalicelib/core/insights.py b/api/chalicelib/core/insights.py index 5b3894606..d5caa3a61 100644 --- a/api/chalicelib/core/insights.py +++ b/api/chalicelib/core/insights.py @@ -28,7 +28,6 @@ JOURNEY_TYPES = { } -@dev.timed def journey(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), filters=[], **args): pg_sub_query_subset = __get_constraints(project_id=project_id, data=args, duration=True, main_table="sessions", time_constraint=True) @@ -181,7 +180,6 @@ def __complete_acquisition(rows, start_date, end_date=None): return rows -@dev.timed def users_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): startTimestamp = TimeUTC.trunc_week(startTimestamp) @@ -229,7 +227,6 @@ def users_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endT } -@dev.timed def users_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): @@ -277,7 +274,6 @@ def users_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), en } -@dev.timed def feature_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): @@ -367,7 +363,7 @@ def feature_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), en } -@dev.timed + def feature_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): @@ -460,7 +456,7 @@ def feature_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), } -@dev.timed + def feature_popularity_frequency(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): @@ -525,7 +521,7 @@ def feature_popularity_frequency(project_id, startTimestamp=TimeUTC.now(delta_da return popularity -@dev.timed + def feature_adoption(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): @@ -595,7 +591,7 @@ def feature_adoption(project_id, startTimestamp=TimeUTC.now(delta_days=-70), end "filters": [{"type": "EVENT_TYPE", "value": event_type}, {"type": "EVENT_VALUE", "value": event_value}]} -@dev.timed + def feature_adoption_top_users(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): pg_sub_query = __get_constraints(project_id=project_id, data=args, duration=True, main_table="sessions", @@ -655,7 +651,7 @@ def feature_adoption_top_users(project_id, startTimestamp=TimeUTC.now(delta_days "filters": [{"type": "EVENT_TYPE", "value": event_type}, {"type": "EVENT_VALUE", "value": event_value}]} -@dev.timed + def feature_adoption_daily_usage(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): pg_sub_query = __get_constraints(project_id=project_id, data=args, duration=True, main_table="sessions", @@ -720,7 +716,7 @@ def feature_adoption_daily_usage(project_id, startTimestamp=TimeUTC.now(delta_da "filters": [{"type": "EVENT_TYPE", "value": event_type}, {"type": "EVENT_VALUE", "value": event_value}]} -@dev.timed + def feature_intensity(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): @@ -757,7 +753,7 @@ def feature_intensity(project_id, startTimestamp=TimeUTC.now(delta_days=-70), en return rows -@dev.timed + def users_active(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): @@ -799,7 +795,7 @@ def users_active(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTime return row_users -@dev.timed + def users_power(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): pg_sub_query = __get_constraints(project_id=project_id, time_constraint=True, chart=False, data=args) @@ -824,7 +820,7 @@ def users_power(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimes return helper.dict_to_camel_case(row_users) -@dev.timed + def users_slipping(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): pg_sub_query = __get_constraints(project_id=project_id, data=args, duration=True, main_table="sessions", @@ -889,7 +885,7 @@ def users_slipping(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTi } -@dev.timed + def search(text, feature_type, project_id, platform=None): if not feature_type: resource_type = "ALL" diff --git a/api/chalicelib/core/metadata.py b/api/chalicelib/core/metadata.py index 301503162..2eb62a6b4 100644 --- a/api/chalicelib/core/metadata.py +++ b/api/chalicelib/core/metadata.py @@ -273,7 +273,6 @@ def add_edit_delete(tenant_id, project_id, new_metas): return {"data": get(project_id)} -@dev.timed def get_remaining_metadata_with_count(tenant_id): all_projects = projects.get_projects(tenant_id=tenant_id) results = [] diff --git a/api/chalicelib/core/projects.py b/api/chalicelib/core/projects.py index e4ac36ad8..f6b7704f5 100644 --- a/api/chalicelib/core/projects.py +++ b/api/chalicelib/core/projects.py @@ -41,7 +41,6 @@ def __create(tenant_id, name): return get_project(tenant_id=tenant_id, project_id=project_id, include_gdpr=True) -@dev.timed def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, stack_integrations=False, version=False, last_tracker_version=None): with pg_client.PostgresClient() as cur: diff --git a/api/chalicelib/core/significance.py b/api/chalicelib/core/significance.py index 2e698dcfd..a868ef2d3 100644 --- a/api/chalicelib/core/significance.py +++ b/api/chalicelib/core/significance.py @@ -24,7 +24,7 @@ T_VALUES = {1: 12.706, 2: 4.303, 3: 3.182, 4: 2.776, 5: 2.571, 6: 2.447, 7: 2.36 21: 2.080, 22: 2.074, 23: 2.069, 25: 2.064, 26: 2.060, 27: 2.056, 28: 2.052, 29: 2.045, 30: 2.042} -@dev.timed + def get_stages_and_events(filter_d, project_id) -> List[RealDictRow]: """ Add minimal timestamp @@ -293,7 +293,7 @@ def pearson_corr(x: list, y: list): return r, confidence, False -@dev.timed + def get_transitions_and_issues_of_each_type(rows: List[RealDictRow], all_issues_with_context, first_stage, last_stage): """ Returns two lists with binary values 0/1: @@ -363,7 +363,7 @@ def get_transitions_and_issues_of_each_type(rows: List[RealDictRow], all_issues_ return transitions, errors, all_errors, n_sess_affected -@dev.timed + def get_affected_users_for_all_issues(rows, first_stage, last_stage): """ @@ -415,7 +415,7 @@ def get_affected_users_for_all_issues(rows, first_stage, last_stage): return all_issues_with_context, n_issues_dict, n_affected_users_dict, n_affected_sessions_dict, contexts -@dev.timed + def count_sessions(rows, n_stages): session_counts = {i: set() for i in range(1, n_stages + 1)} for ind, row in enumerate(rows): @@ -467,7 +467,7 @@ def get_stages(stages, rows): return stages_list -@dev.timed + def get_issues(stages, rows, first_stage=None, last_stage=None, drop_only=False): """ @@ -544,7 +544,7 @@ def get_issues(stages, rows, first_stage=None, last_stage=None, drop_only=False) return n_critical_issues, issues_dict, total_drop_due_to_issues -@dev.timed + def get_top_insights(filter_d, project_id): output = [] stages = filter_d.get("events", []) @@ -582,7 +582,7 @@ def get_top_insights(filter_d, project_id): return stages_list, total_drop_due_to_issues -@dev.timed + def get_issues_list(filter_d, project_id, first_stage=None, last_stage=None): output = dict({'critical_issues_count': 0}) stages = filter_d.get("events", []) diff --git a/api/schemas.py b/api/schemas.py index 07455eb72..a0eec022b 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -613,7 +613,12 @@ class SessionSearchFilterSchema(__MixedSearchFilter): return values -class SessionsSearchPayloadSchema(BaseModel): +class _PaginatedSchema(BaseModel): + limit: int = Field(default=200, gt=0, le=200) + page: int = Field(default=1, gt=0) + + +class SessionsSearchPayloadSchema(_PaginatedSchema): events: List[_SessionSearchEventSchema] = Field([]) filters: List[SessionSearchFilterSchema] = Field([]) startDate: int = Field(None) @@ -622,8 +627,6 @@ class SessionsSearchPayloadSchema(BaseModel): order: Literal["asc", "desc"] = Field(default="desc") events_order: Optional[SearchEventOrder] = Field(default=SearchEventOrder._then) group_by_user: bool = Field(default=False) - limit: int = Field(default=200, gt=0, le=200) - page: int = Field(default=1, gt=0) bookmarked: bool = Field(default=False) class Config: @@ -803,7 +806,7 @@ class TimeseriesMetricOfType(str, Enum): session_count = "sessionCount" -class CustomMetricSessionsPayloadSchema(FlatSessionsSearch): +class CustomMetricSessionsPayloadSchema(FlatSessionsSearch, _PaginatedSchema): startTimestamp: int = Field(TimeUTC.now(-7)) endTimestamp: int = Field(TimeUTC.now()) series: Optional[List[CustomMetricCreateSeriesSchema]] = Field(default=None) From 8d1aaf1c5f181485bc4f58ac34d5b38cb907ed6c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 14:30:54 +0200 Subject: [PATCH 192/294] feat(api): FOSS refactored metrics --- api/chalicelib/core/dashboard.py | 167 +++++++++++++------------------ 1 file changed, 68 insertions(+), 99 deletions(-) diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/dashboard.py index 0b7765b76..fb8241440 100644 --- a/api/chalicelib/core/dashboard.py +++ b/api/chalicelib/core/dashboard.py @@ -134,16 +134,15 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, chart=True, data=args) with pg_client.PostgresClient() as cur: - pg_query = f"""\ - SELECT generated_timestamp AS timestamp, - 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 - WHERE {" AND ".join(pg_sub_query_chart)} - ) AS sessions ON (TRUE) - GROUP BY generated_timestamp - ORDER BY generated_timestamp;""" + pg_query = f"""SELECT generated_timestamp AS timestamp, + 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 + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS sessions ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp;""" params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} cur.execute(cur.mogrify(pg_query, params)) @@ -157,8 +156,7 @@ def get_processed_sessions(project_id, startTimestamp=TimeUTC.now(delta_days=-1) endTimestamp = startTimestamp startTimestamp = endTimestamp - diff - pg_query = f"""\ - SELECT COUNT(sessions.session_id) AS count + pg_query = f"""SELECT COUNT(sessions.session_id) AS count FROM public.sessions WHERE {" AND ".join(pg_sub_query)};""" params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -205,8 +203,8 @@ def get_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimesta cur.execute(cur.mogrify(pg_query, params)) rows = cur.fetchall() results = { - "count": 0 if len(rows) == 0 else __count_distinct_errors(cur, project_id, startTimestamp, endTimestamp, - pg_sub_query_subset), + "count": 0 if len(rows) == 0 else \ + __count_distinct_errors(cur, project_id, startTimestamp, endTimestamp, pg_sub_query_subset), "impactedSessions": sum([r["count"] for r in rows]), "chart": rows } @@ -352,10 +350,9 @@ def __get_application_activity(cur, project_id, startTimestamp, endTimestamp, ** 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 avg_page_load_time - FROM events.pages INNER JOIN public.sessions USING (session_id) - WHERE {" AND ".join(pg_sub_query)};""" + pg_query = f"""SELECT COALESCE(AVG(pages.load_time) ,0) AS avg_page_load_time + 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)} @@ -365,10 +362,9 @@ def __get_application_activity(cur, project_id, startTimestamp, endTimestamp, ** 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 avg - FROM events.resources INNER JOIN public.sessions USING (session_id) - WHERE {" AND ".join(pg_sub_query)};""" + pg_query = f"""SELECT COALESCE(AVG(resources.duration),0) AS avg + 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)})) @@ -401,12 +397,11 @@ def get_user_activity(project_id, startTimestamp=TimeUTC.now(delta_days=-1), def __get_user_activity(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 avg_visited_pages, - COALESCE(AVG(NULLIF(sessions.duration,0)),0) AS avg_session_duration - FROM public.sessions - WHERE {" AND ".join(pg_sub_query)};""" + pg_sub_query.append("(sessions.pages_count>0 OR sessions.duration>0)") + pg_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 + FROM public.sessions + WHERE {" AND ".join(pg_sub_query)};""" params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} @@ -451,8 +446,7 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), COALESCE(AVG(duration), 0) AS avg_duration FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT resources.duration - FROM events.resources - INNER JOIN public.sessions USING (session_id) + FROM events.resources INNER JOIN public.sessions USING (session_id) WHERE {" AND ".join(pg_sub_query_chart)} ) AS sessions ON (TRUE) GROUP BY generated_timestamp @@ -640,7 +634,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, WHERE {" AND ".join(pg_sub_query)} ORDER BY url, type ASC) AS ranked_values WHERE ranked_values.r<=5;""" - print(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text)})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text)})) rows = cur.fetchall() rows = [{"value": i["value"], "type": __get_resource_type_from_db_type(i["key"])} for i in rows] @@ -660,9 +653,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, FROM events.pages INNER JOIN public.sessions USING(session_id) 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})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text.lower()), "platform_0": platform})) @@ -679,10 +669,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, FROM events.resources INNER JOIN public.sessions USING (session_id) 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})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "resource_type": resource_type, @@ -697,9 +683,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, FROM events.pages INNER JOIN public.sessions USING (session_id) 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})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "platform_0": platform})) @@ -711,9 +694,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, FROM events.inputs INNER JOIN public.sessions USING (session_id) 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})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "platform_0": platform})) @@ -725,9 +705,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, FROM events.clicks INNER JOIN public.sessions USING (session_id) 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})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "platform_0": platform})) @@ -746,9 +723,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, FROM sessions 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})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "key": key, "platform_0": platform})) @@ -773,10 +747,6 @@ def search(text, resource_type, project_id, performance=False, pages_only=False, AND sessions.{SESSIONS_META_FIELDS[k]} ILIKE %(value)s 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})) cur.execute(cur.mogrify(pg_query, {"project_id": project_id, "value": helper.string_to_sql_like(text), "key": key, @@ -1491,10 +1461,11 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), pg_sub_query = __get_constraints(project_id=project_id, data=args) pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, chart=True, data=args) - + pg_sub_query.append("performance.avg_fps>0") + pg_sub_query_chart.append("performance.avg_fps>0") with pg_client.PostgresClient() as cur: pg_query = f"""SELECT generated_timestamp AS timestamp, - COALESCE(AVG(NULLIF(performance.avg_fps,0)),0) AS value + COALESCE(AVG(performance.avg_fps),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT avg_fps @@ -1509,7 +1480,7 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "endTimestamp": endTimestamp, **__get_constraint_values(args)} cur.execute(cur.mogrify(pg_query, params)) rows = cur.fetchall() - pg_query = f"""SELECT COALESCE(AVG(NULLIF(performance.avg_fps,0)),0) AS avg + pg_query = f"""SELECT COALESCE(AVG(performance.avg_fps),0) AS avg FROM events.performance INNER JOIN public.sessions USING (session_id) WHERE {" AND ".join(pg_sub_query)};""" cur.execute(cur.mogrify(pg_query, params)) @@ -1843,7 +1814,7 @@ def get_calls_errors(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endT with pg_client.PostgresClient() as cur: pg_query = f"""SELECT resources.method, resources.url_hostpath, - COUNT(resources.session_id) AS all_requests, + COUNT(resources.session_id) AS all_requests, SUM(CASE WHEN resources.status/100 = 4 THEN 1 ELSE 0 END) AS _4xx, SUM(CASE WHEN resources.status/100 = 5 THEN 1 ELSE 0 END) AS _5xx FROM events.resources INNER JOIN sessions USING (session_id) @@ -1868,7 +1839,7 @@ def get_calls_errors_4xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with pg_client.PostgresClient() as cur: pg_query = f"""SELECT resources.method, resources.url_hostpath, - COUNT(resources.session_id) AS all_requests + COUNT(resources.session_id) AS all_requests FROM events.resources INNER JOIN sessions USING (session_id) WHERE {" AND ".join(pg_sub_query)} GROUP BY resources.method, resources.url_hostpath @@ -1891,7 +1862,7 @@ def get_calls_errors_5xx(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with pg_client.PostgresClient() as cur: pg_query = f"""SELECT resources.method, resources.url_hostpath, - COUNT(resources.session_id) AS all_requests + COUNT(resources.session_id) AS all_requests FROM events.resources INNER JOIN sessions USING (session_id) WHERE {" AND ".join(pg_sub_query)} GROUP BY resources.method, resources.url_hostpath @@ -1940,7 +1911,7 @@ def get_errors_per_type(project_id, startTimestamp=TimeUTC.now(delta_days=-1), e WHERE {" AND ".join(pg_sub_query_subset_e)} AND source = 'js_exception' ) - SELECT generated_timestamp AS timestamp, + SELECT generated_timestamp AS timestamp, COALESCE(SUM(CASE WHEN status / 100 = 4 THEN 1 ELSE 0 END), 0) AS _4xx, COALESCE(SUM(CASE WHEN status / 100 = 5 THEN 1 ELSE 0 END), 0) AS _5xx, COALESCE((SELECT COUNT(*) @@ -2248,10 +2219,9 @@ def __get_application_activity_avg_image_load_time(cur, project_id, startTimesta 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)};""" + 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)})) @@ -2322,10 +2292,9 @@ def __get_application_activity_avg_page_load_time(cur, project_id, startTimestam 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)};""" + 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)} @@ -2390,10 +2359,9 @@ def __get_application_activity_avg_request_load_time(cur, project_id, startTimes 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)};""" + 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)})) @@ -2483,7 +2451,7 @@ def __get_page_metrics_avg_dom_content_load_start(cur, project_id, startTimestam 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 + pg_query = f"""SELECT COALESCE(AVG(pages.dom_content_loaded_time), 0) AS value FROM (SELECT pages.dom_content_loaded_time FROM events.pages INNER JOIN public.sessions USING (session_id) @@ -2553,7 +2521,7 @@ def __get_page_metrics_avg_first_contentful_pixel(cur, project_id, startTimestam 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 + pg_query = f"""SELECT COALESCE(AVG(pages.first_contentful_paint_time), 0) AS value FROM (SELECT pages.first_contentful_paint_time FROM events.pages INNER JOIN public.sessions USING (session_id) @@ -2620,11 +2588,10 @@ def get_user_activity_avg_visited_pages(project_id, startTimestamp=TimeUTC.now(d 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)};""" + pg_sub_query.append("sessions.pages_count>0") + pg_query = f"""SELECT COALESCE(CEIL(AVG(sessions.pages_count)),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)} @@ -2684,11 +2651,11 @@ def get_user_activity_avg_session_duration(project_id, startTimestamp=TimeUTC.no 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)};""" + pg_sub_query.append("sessions.duration IS NOT NULL") + pg_sub_query.append("sessions.duration > 0") + pg_query = f"""SELECT COALESCE(AVG(sessions.duration),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)} @@ -2815,7 +2782,8 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no pg_sub_query = __get_constraints(project_id=project_id, data=args) pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, chart=True, data=args) - + pg_sub_query.append("pages.dom_content_loaded_time>0") + pg_sub_query_chart.append("pages.dom_content_loaded_time>0") if value is not None: pg_sub_query.append("pages.path = %(value)s") pg_sub_query_chart.append("pages.path = %(value)s") @@ -2836,7 +2804,7 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no row = cur.fetchone() pg_query = f"""SELECT generated_timestamp AS timestamp, - COALESCE(AVG(NULLIF(pages.dom_content_loaded_time,0)),0) AS value + COALESCE(AVG(pages.dom_content_loaded_time),0) AS value FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp LEFT JOIN LATERAL ( SELECT dom_content_loaded_time @@ -2900,17 +2868,18 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n pg_sub_query_chart = __get_constraints(project_id=project_id, time_constraint=True, chart=True, data=args) + pg_sub_query.append("pages.time_to_interactive > 0") + pg_sub_query_chart.append("pages.time_to_interactive > 0") if value is not None: pg_sub_query.append("pages.path = %(value)s") pg_sub_query_chart.append("pages.path = %(value)s") with pg_client.PostgresClient() as cur: - pg_query = f"""SELECT COALESCE(AVG(NULLIF(pages.time_to_interactive,0)), 0) AS value + 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;""" + AND pages.timestamp < %(endTimestamp)s;""" params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -2918,15 +2887,15 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n cur.execute(cur.mogrify(pg_query, params)) row = cur.fetchone() pg_query = f"""SELECT generated_timestamp AS timestamp, - COALESCE(AVG(NULLIF(pages.time_to_interactive,0)),0) AS value - FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp - LEFT JOIN LATERAL ( - SELECT time_to_interactive - FROM events.pages INNER JOIN public.sessions USING (session_id) - WHERE {" AND ".join(pg_sub_query_chart)} - ) AS pages ON (TRUE) - GROUP BY generated_timestamp - ORDER BY generated_timestamp ASC;""" + COALESCE(AVG(pages.time_to_interactive),0) AS value + FROM generate_series(%(startTimestamp)s, %(endTimestamp)s, %(step_size)s) AS generated_timestamp + LEFT JOIN LATERAL ( + SELECT time_to_interactive + FROM events.pages INNER JOIN public.sessions USING (session_id) + WHERE {" AND ".join(pg_sub_query_chart)} + ) AS pages ON (TRUE) + GROUP BY generated_timestamp + ORDER BY generated_timestamp ASC;""" cur.execute(cur.mogrify(pg_query, params)) rows = cur.fetchall() row["chart"] = helper.list_to_camel_case(rows) From 3fdd08e327f75a7111d3aec02da8a20d5394393f Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 14:47:38 +0200 Subject: [PATCH 193/294] fix(ui) - json fix --- .../Dashboard/components/DashboardModal/DashboardModal.tsx | 4 +++- frontend/app/mstore/types/dashboard.ts | 1 - frontend/app/mstore/types/filterItem.ts | 3 ++- frontend/app/mstore/types/widget.ts | 3 +-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 35277dc74..15b26c809 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -22,7 +22,9 @@ function DashboardModal(props) { const loading = useObserver(() => dashboardStore.isSaving); const onSave = () => { - dashboardStore.save(dashboard).then(hideModal) + dashboardStore.save(dashboard).then(hideModal).then(() => { + dashboardStore.fetch(dashboard.dashboardId) + }) } const handleCreateNew = () => { diff --git a/frontend/app/mstore/types/dashboard.ts b/frontend/app/mstore/types/dashboard.ts index 25cfa7a05..57b191252 100644 --- a/frontend/app/mstore/types/dashboard.ts +++ b/frontend/app/mstore/types/dashboard.ts @@ -95,7 +95,6 @@ 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)).sort((a, b) => a.position - b.position) : [] }) return this diff --git a/frontend/app/mstore/types/filterItem.ts b/frontend/app/mstore/types/filterItem.ts index 606767b2b..7b7e4999a 100644 --- a/frontend/app/mstore/types/filterItem.ts +++ b/frontend/app/mstore/types/filterItem.ts @@ -36,7 +36,8 @@ export default class FilterItem { } fromJson(json, mainFilterKey = '') { - let _filter = filtersMap[json.type] + console.log('fromJson', json.type) + let _filter = filtersMap[json.type] || {} if (mainFilterKey) { const mainFilter = filtersMap[mainFilterKey]; const subFilterMap = {} diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index 0a465f5cf..adff4e390 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -139,8 +139,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 = json.editedAt || json.createdAt ? DateTime.fromMillis(json.editedAt || json.createdAt) : null - this.lastModified = DateTime.fromMillis(1649319074) + this.lastModified = json.editedAt || json.createdAt ? DateTime.fromMillis(json.editedAt || json.createdAt) : null this.config = json.config this.position = json.config.position this.predefinedKey = json.predefinedKey From 0ccf3371f4967f0bf5d993408e4d75159a134100 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 14:52:35 +0200 Subject: [PATCH 194/294] change(ui) - increased page size for metrics --- frontend/app/mstore/metricStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index 48c796179..158644f68 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -42,7 +42,7 @@ export default class MetricStore implements IMetricStore { instance: IWidget = new Widget() page: number = 1 - pageSize: number = 4 + pageSize: number = 15 metricsSearch: string = "" sort: any = {} From 7772205952c71a89881ab3438ea769fcdc63c16c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 15:10:09 +0200 Subject: [PATCH 195/294] feat(api): EE mertics optimized, refactored and fixed --- ee/api/chalicelib/core/dashboard.py | 280 ++++++++++++++-------------- ee/api/chalicelib/core/insights.py | 28 +-- 2 files changed, 154 insertions(+), 154 deletions(-) diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/dashboard.py index 5d25207e5..a39e7ed69 100644 --- a/ee/api/chalicelib/core/dashboard.py +++ b/ee/api/chalicelib/core/dashboard.py @@ -1,5 +1,4 @@ import math -import random import schemas from chalicelib.utils import pg_client @@ -343,12 +342,12 @@ def __get_page_metrics(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_sub_query.append("(pages.dom_content_loaded_event_end>0 OR pages.first_contentful_paint>0)") # 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.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)};""" + 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.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, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) @@ -376,10 +375,9 @@ def __get_application_activity(ch, project_id, startTimestamp, endTimestamp, **a meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition - ch_query = f"""\ - SELECT AVG(NULLIF(pages.load_event_end ,0)) AS avg_page_load_time - FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} - WHERE {" AND ".join(ch_sub_query)};""" + ch_query = f"""SELECT AVG(pages.load_event_end) AS avg_page_load_time + FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} + WHERE {" AND ".join(ch_sub_query)} AND pages.load_event_end>0;""" params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} row = ch.execute(query=ch_query, params=params)[0] @@ -389,10 +387,9 @@ def __get_application_activity(ch, project_id, startTimestamp, endTimestamp, **a 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 avg - FROM resources {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} - WHERE {" AND ".join(ch_sub_query)};""" + ch_query = f"""SELECT 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)} AND resources.duration>0;""" row = ch.execute(query=ch_query, params={"project_id": project_id, "type": 'img', "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)})[0] @@ -435,12 +432,11 @@ def __get_user_activity(ch, 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 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)};""" + ch_sub_query.append("(sessions.pages_count>0 OR sessions.duration>0)") + 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 + 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)} @@ -464,14 +460,13 @@ 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, + AVG(resources.duration) 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)} + WHERE {" AND ".join(ch_sub_query)} AND resources.duration>0 GROUP BY resources.url ORDER BY avg DESC LIMIT 10;""" 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] @@ -480,17 +475,15 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), 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;""" + ch_query = f"""SELECT url, + 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 resources.duration>0 + 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: sub_rows = [] @@ -547,10 +540,10 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi "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 + 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 resources.type = 'img' + AND resources.type = 'img' AND resources.duration>0 {(f' AND ({" OR ".join(img_constraints)})') if len(img_constraints) > 0 else ""} GROUP BY timestamp ORDER BY timestamp;""" @@ -560,10 +553,10 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi 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 + 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 resources.type = 'fetch' + AND resources.type = 'fetch' AND resources.duration>0 {(f' AND ({" OR ".join(request_constraints)})') if len(request_constraints) > 0 else ""} GROUP BY timestamp ORDER BY timestamp;""" @@ -578,9 +571,9 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi 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 + AVG(pages.load_event_end) AS avg FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} - WHERE {" AND ".join(ch_sub_query_chart)} + WHERE {" AND ".join(ch_sub_query_chart)} AND pages.load_event_end>0 {(f' AND ({" OR ".join(location_constraints)})') if len(location_constraints) > 0 else ""} GROUP BY timestamp ORDER BY timestamp;""" @@ -899,10 +892,11 @@ def get_resources_loading_time(project_id, startTimestamp=TimeUTC.now(delta_days ch_sub_query_chart.append(f"resources.url = %(value)s") meta_condition = __get_meta_constraint(args) ch_sub_query_chart += meta_condition + ch_sub_query_chart.append("resources.duration>0") 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 + 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 @@ -912,7 +906,7 @@ def get_resources_loading_time(project_id, startTimestamp=TimeUTC.now(delta_days "endTimestamp": endTimestamp, "value": url, "type": type, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(NULLIF(resources.duration,0)) AS avg + ch_query = f"""SELECT 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)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 @@ -978,16 +972,16 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), 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)} + WHERE {" AND ".join(ch_sub_query)} GROUP BY name ORDER BY avg DESC LIMIT 10;""" 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) - + if len(rows) == 0: + return [] ch_sub_query.append(ch_sub_query_chart[-1]) results = [] names = {f"name_{i}": r["name"] for i, r in enumerate(rows)} @@ -995,7 +989,7 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), 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)} + 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;""" @@ -1003,7 +997,6 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "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: sub_chart = [] @@ -1281,7 +1274,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 value + 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 @@ -2016,8 +2009,7 @@ def get_resources_vs_visually_complete(project_id, startTimestamp=TimeUTC.now(de return helper.list_to_camel_case( __merge_charts( [{"timestamp": i["timestamp"], "avgCountResources": i["avg"], "types": i["types"]} for i in resources], - [{"timestamp": i["timestamp"], "avgTimeToRender": i["avg"]} for i in - time_to_render["chart"]])) + [{"timestamp": i["timestamp"], "avgTimeToRender": i["value"]} for i in time_to_render["chart"]])) def get_resources_count_by_type(project_id, startTimestamp=TimeUTC.now(delta_days=-1), @@ -2118,11 +2110,10 @@ def __get_application_activity_avg_page_load_time(ch, project_id, startTimestamp 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)};""" + ch_sub_query.append("pages.load_event_end>0") + ch_query = f"""SELECT AVG(pages.load_event_end) 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] @@ -2154,26 +2145,25 @@ def get_performance_avg_page_load_time(ch, project_id, startTimestamp=TimeUTC.no ch_sub_query_chart = __get_basic_constraints(table_name="pages", round_start=True, data=args) ch_sub_query_chart += meta_condition + ch_sub_query_chart.append("pages.load_event_end>0") 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 + COALESCE(AVG(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})] + rows = ch.execute(query=ch_query, params={**params, **location_constraints_vals, **__get_constraint_values(args)}) + pages = __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 + # for s in pages: + # for k in s: + # if s[k] is None: + # s[k] = 0 return pages @@ -2198,17 +2188,18 @@ def __get_application_activity_avg_image_load_time(ch, project_id, startTimestam meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition ch_sub_query.append("resources.type= %(type)s") + ch_sub_query.append("resources.duration>0") ch_query = f"""\ - SELECT AVG(NULLIF(resources.duration,0)) AS value + SELECT COALESCE(AVG(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 + # for k in result: + # if result[k] is None: + # result[k] = 0 return result @@ -2231,8 +2222,9 @@ def get_performance_avg_image_load_time(ch, project_id, startTimestamp=TimeUTC.n params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp} + ch_sub_query_chart.append("resources.duration>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(resources.duration,0)) AS value + COALESCE(AVG(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' @@ -2240,15 +2232,14 @@ def get_performance_avg_image_load_time(ch, project_id, startTimestamp=TimeUTC.n 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})] + images = __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 + # for s in images: + # for k in s: + # if s[k] is None: + # s[k] = 0 return images @@ -2273,17 +2264,17 @@ def __get_application_activity_avg_request_load_time(ch, project_id, startTimest 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)};""" + ch_sub_query.append("resources.duration>0") + ch_query = f"""SELECT COALESCE(AVG(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 + # for k in result: + # if result[k] is None: + # result[k] = 0 return result @@ -2305,9 +2296,9 @@ def get_performance_avg_request_load_time(ch, project_id, startTimestamp=TimeUTC request_constraints_vals["val_" + str(len(request_constraints) - 1)] = r['value'] params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp} - + ch_sub_query_chart.append("resources.duration>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(resources.duration,0)) AS value + COALESCE(AVG(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' @@ -2316,15 +2307,14 @@ def get_performance_avg_request_load_time(ch, project_id, startTimestamp=TimeUTC 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})] + requests = __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 + # for s in requests: + # for k in s: + # if s[k] is None: + # s[k] = 0 return requests @@ -2352,10 +2342,10 @@ def __get_page_metrics_avg_dom_content_load_start(ch, project_id, startTimestamp 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)};""" + ch_sub_query.append("pages.dom_content_loaded_event_end>0") + ch_query = f"""SELECT COALESCE(AVG(pages.dom_content_loaded_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, "type": 'fetch', "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) @@ -2371,23 +2361,22 @@ def __get_page_metrics_avg_dom_content_load_start_chart(ch, project_id, startTim params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp} - + ch_sub_query_chart.append("pages.dom_content_loaded_event_end>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(NULLIF(pages.dom_content_loaded_event_end,0)) AS value + COALESCE(AVG(pages.dom_content_loaded_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)} GROUP BY timestamp ORDER BY timestamp;""" rows = ch.execute(query=ch_query, params={**params, **__get_constraint_values(args)}) - rows = [{"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})] + rows = __complete_missing_steps(rows=rows, start_time=startTimestamp, + end_time=endTimestamp, + density=density, neutral={"value": 0}) - for s in rows: - for k in s: - if s[k] is None: - s[k] = 0 + # for s in rows: + # for k in s: + # if s[k] is None: + # s[k] = 0 return rows @@ -2414,9 +2403,10 @@ def __get_page_metrics_avg_first_contentful_pixel(ch, project_id, startTimestamp ch_sub_query = __get_basic_constraints(table_name="pages", data=args) meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition + ch_sub_query.append("pages.first_contentful_paint>0") # 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 + SELECT COALESCE(AVG(pages.first_contentful_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)};""" params = {"project_id": project_id, "type": 'fetch', "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -2434,9 +2424,9 @@ def __get_page_metrics_avg_first_contentful_pixel_chart(ch, project_id, startTim params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp} - + ch_sub_query_chart.append("pages.first_contentful_paint>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(NULLIF(pages.first_contentful_paint,0)),0) AS value + COALESCE(AVG(pages.first_contentful_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_chart)} GROUP BY timestamp @@ -2478,11 +2468,10 @@ def __get_user_activity_avg_visited_pages(ch, project_id, startTimestamp, endTim 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)};""" + ch_sub_query.append("sessions.pages_count>0") + ch_query = f"""SELECT COALESCE(CEIL(AVG(sessions.pages_count)),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)} @@ -2499,9 +2488,9 @@ def __get_user_activity_avg_visited_pages_chart(ch, project_id, startTimestamp, params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp} - + ch_sub_query_chart.append("sessions.pages_count>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(sessions.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(NULLIF(sessions.pages_count,0)),0) AS value + COALESCE(AVG(sessions.pages_count),0) 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 @@ -2545,10 +2534,9 @@ def __get_user_activity_avg_session_duration(ch, project_id, startTimestamp, end ch_sub_query.append("isNotNull(sessions.duration)") ch_sub_query.append("sessions.duration>0") - 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)};""" + ch_query = f"""SELECT COALESCE(AVG(sessions.duration),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)} @@ -2707,10 +2695,14 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no if value is not None: ch_sub_query.append("pages.url_path = %(value)s") ch_sub_query_chart.append("pages.url_path = %(value)s") + ch_sub_query.append("isNotNull(pages.dom_content_loaded_event_time)") + ch_sub_query.append("pages.dom_content_loaded_event_time>0") + ch_sub_query_chart.append("isNotNull(pages.dom_content_loaded_event_time)") + ch_sub_query_chart.append("pages.dom_content_loaded_event_time>0") 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;""" + WHERE {" AND ".join(ch_sub_query)};""" params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -2718,11 +2710,11 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no rows = ch.execute(query=ch_query, params=params) results = helper.dict_to_camel_case(rows[0]) ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COALESCE(AVG(NULLIF(pages.dom_content_loaded_event_time ,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_chart)} - GROUP BY timestamp - ORDER BY timestamp;;""" + 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_chart)} + GROUP BY timestamp + ORDER BY timestamp;""" rows = ch.execute(query=ch_query, params=params) results["chart"] = helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, @@ -2745,10 +2737,14 @@ def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(de if value is not None: ch_sub_query.append("pages.url_path = %(value)s") ch_sub_query_chart.append("pages.url_path = %(value)s") + ch_sub_query.append("isNotNull(pages.ttfb)") + ch_sub_query.append("pages.ttfb>0") + ch_sub_query_chart.append("isNotNull(pages.ttfb)") + ch_sub_query_chart.append("pages.ttfb>0") 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;""" + WHERE {" AND ".join(ch_sub_query)};""" params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -2756,11 +2752,11 @@ def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(de rows = ch.execute(query=ch_query, params=params) results = rows[0] ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COALESCE(AVG(NULLIF(pages.ttfb ,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_chart)} AND isNotNull(pages.ttfb) AND pages.ttfb>0 - GROUP BY timestamp - ORDER BY timestamp;""" + 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_chart)} + GROUP BY timestamp + ORDER BY timestamp;""" rows = ch.execute(query=ch_query, params=params) results["chart"] = helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, @@ -2783,10 +2779,14 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n if value is not None: ch_sub_query.append("pages.url_path = %(value)s") ch_sub_query_chart.append("pages.url_path = %(value)s") + ch_sub_query.append("isNotNull(pages.time_to_interactive)") + ch_sub_query.append("pages.time_to_interactive >0") + ch_sub_query_chart.append("isNotNull(pages.time_to_interactive)") + ch_sub_query_chart.append("pages.time_to_interactive >0") 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;""" + WHERE {" AND ".join(ch_sub_query)};""" params = {"step_size": step_size, "project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -2794,11 +2794,11 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n rows = ch.execute(query=ch_query, params=params) results = rows[0] ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COALESCE(AVG(NULLIF(pages.time_to_interactive ,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_chart)} AND isNotNull(pages.time_to_interactive) AND pages.time_to_interactive >0 - GROUP BY timestamp - ORDER BY timestamp;;""" + 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_chart)} + GROUP BY timestamp + ORDER BY timestamp;""" rows = ch.execute(query=ch_query, params=params) results["chart"] = helper.list_to_camel_case(__complete_missing_steps(rows=rows, start_time=startTimestamp, end_time=endTimestamp, diff --git a/ee/api/chalicelib/core/insights.py b/ee/api/chalicelib/core/insights.py index ff9d4dad4..9ac11013d 100644 --- a/ee/api/chalicelib/core/insights.py +++ b/ee/api/chalicelib/core/insights.py @@ -29,7 +29,7 @@ JOURNEY_TYPES = { } -@dev.timed + def journey(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTimestamp=TimeUTC.now(), filters=[], **args): event_start = None event_table = JOURNEY_TYPES["CLICK"]["table"] @@ -190,7 +190,7 @@ def __complete_acquisition(rows, start_date, end_date=None): return rows -@dev.timed + def users_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): startTimestamp = TimeUTC.trunc_week(startTimestamp) @@ -233,7 +233,7 @@ def users_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endT } -@dev.timed + def users_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): startTimestamp = TimeUTC.trunc_week(startTimestamp) @@ -286,7 +286,7 @@ def users_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), en } -@dev.timed + def feature_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): startTimestamp = TimeUTC.trunc_week(startTimestamp) @@ -386,7 +386,7 @@ def feature_retention(project_id, startTimestamp=TimeUTC.now(delta_days=-70), en } -@dev.timed + def feature_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): startTimestamp = TimeUTC.trunc_week(startTimestamp) @@ -497,7 +497,7 @@ def feature_acquisition(project_id, startTimestamp=TimeUTC.now(delta_days=-70), } -@dev.timed + def feature_popularity_frequency(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): startTimestamp = TimeUTC.trunc_week(startTimestamp) @@ -572,7 +572,7 @@ def feature_popularity_frequency(project_id, startTimestamp=TimeUTC.now(delta_da return popularity -@dev.timed + def feature_adoption(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): event_type = "CLICK" @@ -658,7 +658,7 @@ def feature_adoption(project_id, startTimestamp=TimeUTC.now(delta_days=-70), end "filters": [{"type": "EVENT_TYPE", "value": event_type}, {"type": "EVENT_VALUE", "value": event_value}]} -@dev.timed + def feature_adoption_top_users(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): event_type = "CLICK" @@ -728,7 +728,7 @@ def feature_adoption_top_users(project_id, startTimestamp=TimeUTC.now(delta_days "filters": [{"type": "EVENT_TYPE", "value": event_type}, {"type": "EVENT_VALUE", "value": event_value}]} -@dev.timed + def feature_adoption_daily_usage(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): event_type = "CLICK" @@ -796,7 +796,7 @@ def feature_adoption_daily_usage(project_id, startTimestamp=TimeUTC.now(delta_da "filters": [{"type": "EVENT_TYPE", "value": event_type}, {"type": "EVENT_VALUE", "value": event_value}]} -@dev.timed + def feature_intensity(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): event_table = JOURNEY_TYPES["CLICK"]["table"] @@ -838,7 +838,7 @@ PERIOD_TO_FUNCTION = { } -@dev.timed + def users_active(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): meta_condition = __get_meta_constraint(args) @@ -885,7 +885,7 @@ def users_active(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTime return {"avg": avg, "chart": rows} -@dev.timed + def users_power(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): ch_sub_query = __get_basic_constraints(table_name="sessions_metadata", data=args) meta_condition = __get_meta_constraint(args) @@ -925,7 +925,7 @@ def users_power(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimes return {"avg": avg, "partition": helper.list_to_camel_case(rows)} -@dev.timed + def users_slipping(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTimestamp=TimeUTC.now(), filters=[], **args): ch_sub_query = __get_basic_constraints(table_name="feature", data=args) @@ -1008,7 +1008,7 @@ def users_slipping(project_id, startTimestamp=TimeUTC.now(delta_days=-70), endTi } -@dev.timed + def search(text, feature_type, project_id, platform=None): if not feature_type: resource_type = "ALL" From d566663343d0b45fd6851bf6bf3aef9b03bfb11c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 15:11:50 +0200 Subject: [PATCH 196/294] change(ui) - jump on drag --- .../app/components/Session_/Player/Controls/Circle.tsx | 2 -- .../components/Session_/Player/Controls/Timeline.js | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/frontend/app/components/Session_/Player/Controls/Circle.tsx b/frontend/app/components/Session_/Player/Controls/Circle.tsx index b49d23216..76740c73e 100644 --- a/frontend/app/components/Session_/Player/Controls/Circle.tsx +++ b/frontend/app/components/Session_/Player/Controls/Circle.tsx @@ -5,11 +5,9 @@ interface Props { preview?: boolean; } export const Circle: FC = memo(function Box({ preview }) { - // const backgroundColor = yellow ? 'yellow' : 'white' return (
) diff --git a/frontend/app/components/Session_/Player/Controls/Timeline.js b/frontend/app/components/Session_/Player/Controls/Timeline.js index c22f5463e..69b228d7d 100644 --- a/frontend/app/components/Session_/Player/Controls/Timeline.js +++ b/frontend/app/components/Session_/Player/Controls/Timeline.js @@ -77,8 +77,6 @@ const getPointerIcon = (type) => { }), { setTimelinePointer }) export default class Timeline extends React.PureComponent { progressRef = React.createRef() - progressWidth = 0 - seekTime = 0 wasPlaying = false seekProgress = (e) => { @@ -95,9 +93,8 @@ export default class Timeline extends React.PureComponent { } componentDidMount() { - const { issues, events, fetchList, skipToIssue } = this.props; + const { issues, skipToIssue } = this.props; const firstIssue = issues.get(0); - this.progressWidth = this.progressRef.current.offsetWidth; if (firstIssue && skipToIssue) { this.props.jump(firstIssue.time); @@ -105,7 +102,6 @@ export default class Timeline extends React.PureComponent { } onDragEnd = (item, monitor) => { - this.props.jump(this.seekTime); if (this.wasPlaying) { this.props.togglePlay(); } @@ -116,7 +112,7 @@ export default class Timeline extends React.PureComponent { const p = (offset.x - 60) / this.progressRef.current.offsetWidth; const time = Math.max(Math.round(p * endTime), 0); - this.seekTime = time; + this.props.jump(time); if (this.props.playing) { this.wasPlaying = true; this.props.pause(); @@ -137,7 +133,7 @@ export default class Timeline extends React.PureComponent { clickRageTime, stackList, fetchList, - issues + issues, } = this.props; const scale = 100 / endTime; From 57c027cf82355c0396433ab73bc391b3e5bc4cc1 Mon Sep 17 00:00:00 2001 From: KRAIEM Taha Yassine Date: Fri, 15 Apr 2022 13:22:29 +0000 Subject: [PATCH 197/294] changed version number --- scripts/helmcharts/init.sh | 2 +- scripts/helmcharts/openreplay/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/alerts/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/assets/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/assist/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/chalice/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/db/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/ender/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/http/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/integrations/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/peers/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/sink/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/storage/Chart.yaml | 2 +- scripts/helmcharts/vars.yaml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/helmcharts/init.sh b/scripts/helmcharts/init.sh index 078723007..f884c1c29 100644 --- a/scripts/helmcharts/init.sh +++ b/scripts/helmcharts/init.sh @@ -15,7 +15,7 @@ fatal() exit 1 } -version="v1.5.4" +version="v1.5.5" usr=`whoami` # Installing k3s diff --git a/scripts/helmcharts/openreplay/Chart.yaml b/scripts/helmcharts/openreplay/Chart.yaml index 827f8566e..4b8e3e6a7 100644 --- a/scripts/helmcharts/openreplay/Chart.yaml +++ b/scripts/helmcharts/openreplay/Chart.yaml @@ -22,7 +22,7 @@ version: 0.1.0 # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. # Ref: https://github.com/helm/helm/issues/7858#issuecomment-608114589 -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" dependencies: - name: ingress-nginx diff --git a/scripts/helmcharts/openreplay/charts/alerts/Chart.yaml b/scripts/helmcharts/openreplay/charts/alerts/Chart.yaml index 866cf72c2..4d99d566b 100644 --- a/scripts/helmcharts/openreplay/charts/alerts/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/alerts/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/assets/Chart.yaml b/scripts/helmcharts/openreplay/charts/assets/Chart.yaml index d72174f29..19034cb87 100644 --- a/scripts/helmcharts/openreplay/charts/assets/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/assets/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/assist/Chart.yaml b/scripts/helmcharts/openreplay/charts/assist/Chart.yaml index 0bcde4fd3..fda8cdfa0 100644 --- a/scripts/helmcharts/openreplay/charts/assist/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/chalice/Chart.yaml b/scripts/helmcharts/openreplay/charts/chalice/Chart.yaml index 9f09c00db..7f2ef368d 100644 --- a/scripts/helmcharts/openreplay/charts/chalice/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/chalice/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/db/Chart.yaml b/scripts/helmcharts/openreplay/charts/db/Chart.yaml index 8b775b934..a938ef054 100644 --- a/scripts/helmcharts/openreplay/charts/db/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/db/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/ender/Chart.yaml b/scripts/helmcharts/openreplay/charts/ender/Chart.yaml index cd206be2a..561c48726 100644 --- a/scripts/helmcharts/openreplay/charts/ender/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/ender/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/http/Chart.yaml b/scripts/helmcharts/openreplay/charts/http/Chart.yaml index 729ef006f..c28fc23b4 100644 --- a/scripts/helmcharts/openreplay/charts/http/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/http/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/integrations/Chart.yaml b/scripts/helmcharts/openreplay/charts/integrations/Chart.yaml index e0ecdb66b..c8c405266 100644 --- a/scripts/helmcharts/openreplay/charts/integrations/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/integrations/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/peers/Chart.yaml b/scripts/helmcharts/openreplay/charts/peers/Chart.yaml index b482f5986..0fbf35cf5 100644 --- a/scripts/helmcharts/openreplay/charts/peers/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/peers/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/sink/Chart.yaml b/scripts/helmcharts/openreplay/charts/sink/Chart.yaml index 83bcac763..a9d2cd310 100644 --- a/scripts/helmcharts/openreplay/charts/sink/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/sink/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/openreplay/charts/storage/Chart.yaml b/scripts/helmcharts/openreplay/charts/storage/Chart.yaml index cc572ffe1..667e6046d 100644 --- a/scripts/helmcharts/openreplay/charts/storage/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/storage/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.4" +AppVersion: "v1.5.5" diff --git a/scripts/helmcharts/vars.yaml b/scripts/helmcharts/vars.yaml index 7f5aaf424..49d48525b 100644 --- a/scripts/helmcharts/vars.yaml +++ b/scripts/helmcharts/vars.yaml @@ -1,4 +1,4 @@ -fromVersion: "v1.5.4" +fromVersion: "v1.5.5" # Databases specific variables postgresql: &postgres # For generating passwords From 509897e582b1cc18ab680ec804656d76b72a1bd0 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 15:42:59 +0200 Subject: [PATCH 198/294] change(ui) - siteId --- .../components/Client/Sites/NewSiteForm.js | 47 +++++++++---------- frontend/app/duck/site.js | 6 +-- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/frontend/app/components/Client/Sites/NewSiteForm.js b/frontend/app/components/Client/Sites/NewSiteForm.js index b98a77a38..407da1021 100644 --- a/frontend/app/components/Client/Sites/NewSiteForm.js +++ b/frontend/app/components/Client/Sites/NewSiteForm.js @@ -39,12 +39,11 @@ export default class NewSiteForm extends React.PureComponent { } else { this.props.save(this.props.site).then(() => { const { sites } = this.props; - const site = sites.last(); - - this.props.pushNewSite(site) - if (!pathname.includes('/client')) { - this.props.setSiteId(site.id) - } + const site = sites.last(); + this.props.pushNewSite(site) + if (!pathname.includes('/client')) { + this.props.setSiteId(site.id) + } this.props.onClose(null, site) }); } @@ -59,17 +58,17 @@ export default class NewSiteForm extends React.PureComponent { const { site, loading } = this.props; return (
-
+
- - -
+ + +
- { this.state.existsError && -
- { "Site exists already. Please choose another one." } -
- } -
- - ); + { this.state.existsError && +
+ { "Site exists already. Please choose another one." } +
+ } +
+ + ); } } \ No newline at end of file diff --git a/frontend/app/duck/site.js b/frontend/app/duck/site.js index 5530773f0..ff5142804 100644 --- a/frontend/app/duck/site.js +++ b/frontend/app/duck/site.js @@ -53,12 +53,12 @@ const reducer = (state = initialState, action = {}) => { return state.setIn([ 'instance', 'gdpr' ], gdpr); case FETCH_LIST_SUCCESS: let siteId = state.get("siteId"); - if (!siteId) { - siteId = !!action.data.find(s => s.projectId === storedSiteId) + const siteExists = action.data.map(s => s.projectId).includes(siteId); + if (!siteId || !siteExists) { + siteId = !!action.data.find(s => s.projectId === parseInt(storedSiteId)) ? storedSiteId : action.data[0].projectId; } - console.log('siteId asd', siteId) return state.set('list', List(action.data.map(Site))).set('siteId', siteId); case SET_SITE_ID: localStorage.setItem(SITE_ID_STORAGE_KEY, action.siteId) From ef004c0f4627c5c8e44ccedc0e13ba01f1a101e7 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 15:48:21 +0200 Subject: [PATCH 199/294] change(ui) - removed logs --- .../BugFinder/CustomFilters/FilterModal.js | 2 - .../app/components/Dashboard/NewDashboard.tsx | 10 +- .../CustomMetricTable/CustomMetricTable.tsx | 1 - .../CallWithErrors/CallWithErrors.tsx | 2 - .../DomBuildingTime/DomBuildingTime.tsx | 2 +- .../ResourceLoadingTime.tsx | 2 +- .../ResponseTime/ResponseTime.tsx | 2 +- .../SessionsImpactedBySlowRequests.tsx | 2 - .../SpeedIndexByLocation copy.js__ | 114 ------------------ .../TimeToRender/TimeToRender.tsx | 2 +- .../components/WidgetForm/WidgetForm.tsx | 1 - .../WidgetSessions/WidgetSessions.tsx | 1 - .../Session_/Performance/Performance.js | 1 - .../app/components/Session_/Player/Player.js | 2 - .../Attributes/AttributeValueField.js | 1 - frontend/app/duck/sessions.js | 1 - frontend/app/mstore/metricStore.ts | 1 - frontend/app/mstore/types/filterItem.ts | 1 - frontend/app/mstore/types/widget.ts | 1 - 19 files changed, 5 insertions(+), 144 deletions(-) delete mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation copy.js__ diff --git a/frontend/app/components/BugFinder/CustomFilters/FilterModal.js b/frontend/app/components/BugFinder/CustomFilters/FilterModal.js index c5ab474be..5bd536de5 100644 --- a/frontend/app/components/BugFinder/CustomFilters/FilterModal.js +++ b/frontend/app/components/BugFinder/CustomFilters/FilterModal.js @@ -161,8 +161,6 @@ export default class FilterModal extends React.PureComponent { const staticFilters = preloadedFilters .filter(({ value, actualValue }) => !this.props.loading && this.test(actualValue || value)) - // console.log('filteredList', filteredList); - return (!displayed ? null :
{ loading && diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 6b77ff830..93f3cda8e 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -18,15 +18,7 @@ function NewDashboard(props) { dashboardStore.fetchList().then((resp) => { if (parseInt(dashboardId) > 0) { dashboardStore.selectDashboardById(dashboardId); - } - // else { - // dashboardStore.selectDefaultDashboard().then(({ dashboardId }) => { - // console.log('dashboardId', dashboardId) - // // if (!history.location.pathname.includes('/metrics')) { - // // history.push(withSiteId(dashboardSelected(dashboardId), siteId)); - // // } - // }); - // } + } }); }, [siteId]); diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx index f83f48466..a43a35fc8 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricTable/CustomMetricTable.tsx @@ -33,7 +33,6 @@ function CustomMetriTable(props: Props) { const rows = List(data.values); const onClickHandler = (event, data) => { - console.log('onClickHandler', data); const filters = Array(); let filter = { ...filtersMap[metric.metricOf] } filter.value = [data.name] diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/CallWithErrors.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/CallWithErrors.tsx index 333fca287..4e5d0f637 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/CallWithErrors.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/CallWithErrors/CallWithErrors.tsx @@ -52,8 +52,6 @@ function CallWithErrors(props: Props) { const test = (value = '', serach) => getRE(serach, 'i').test(value); const _data = search ? metric.data.chart.filter(i => test(i.urlHostpath, search)) : metric.data.chart.images; - console.log('data', metric.data) - const write = ({ target: { name, value } }) => { setSearch(value) }; diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx index a551671dc..11c71c34d 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx @@ -26,7 +26,7 @@ function DomBuildingTime(props: Props) { const onSelect = (params) => { // const _params = { density: 70 } - console.log('params', params) // TODO reload the data with new params; + // TODO reload the data with new params; // this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value }) } diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx index d33c96827..63d63d207 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx @@ -38,7 +38,7 @@ function ResourceLoadingTime(props: Props) { const onSelect = (params) => { // const _params = { density: 70 } setSutoCompleteSelected(params.value); - console.log('params', params) // TODO reload the data with new params; + // TODO reload the data with new params; // this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value }) } diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx index a9ef6dcac..e1af351ee 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTime/ResponseTime.tsx @@ -27,7 +27,7 @@ function ResponseTime(props: Props) { const onSelect = (params) => { // const _params = { density: 70 } - console.log('params', params) // TODO reload the data with new params; + // TODO reload the data with new params; // this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value }) } diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx index ee227854b..70202ea56 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsImpactedBySlowRequests/SessionsImpactedBySlowRequests.tsx @@ -16,8 +16,6 @@ function SessionsImpactedBySlowRequests(props: Props) { const { data, metric } = props; const gradientDef = Styles.gradientDef(); - console.log('SessionsImpactedBySlowRequests', metric.data) - return ( { - const series: any[] = []; - data.forEach(item => { - const d = [threeLetter[item.userCountry], Math.round(item.avg)] - series.push(d) - }) - - return series; - } - - useEffect(() => { - if (wrapper.current && !map && metric.data.chart.length > 0) { - const dataset = getDataset(); - map = new DataMap({ - element: wrapper.current, - fills: { defaultFill: '#E8E8E8' }, - data: dataset, - // responsive: true, - // height: null, //if not null, datamaps will grab the height of 'element' - // width: null, //if not null, datamaps will grab the width of 'element' - geographyConfig: { - borderColor: '#FFFFFF', - borderWidth: 0.5, - highlightBorderWidth: 1, - popupOnHover: true, - // don't change color on mouse hover - highlightFillColor: function(geo) { - return '#999999'; - // return geo['fillColor'] || '#F5F5F5'; - }, - // only change border - highlightBorderColor: '#B7B7B7', - // show desired information in tooltip - popupTemplate: function(geo, data) { - // don't show tooltip if country don't present in dataset - if (!data) { return ; } - // tooltip content - return ['
', - '', geo.properties.name, '', - 'Avg: ', numberWithCommas(data.numberOfThings), '', - '
'].join(''); - } - } - }); - } - }, []) - - useEffect(() => { - if (map && map.updateChoropleth) { - const series = getSeries(metric.data.chart); - // console.log('series', series) - // map.updateChoropleth(series, {reset: true}); - } - }, []) - - const getDataset = () => { - const { metric } = props; - const colors = Styles.colors; - - var dataset = {}; - const series = getSeries(metric.data.chart); - var onlyValues = series.map(function(obj){ return obj[1]; }); - const paletteScale = colorScale(onlyValues, [...colors].reverse()); - - // fill dataset in appropriate format - series.forEach(function(item){ - var iso = item[0], value = item[1]; - dataset[iso] = { numberOfThings: value, fillColor: paletteScale(value) }; - }); - return dataset; - } - - return ( - -
- -
- -
- - ); -} - -export default observer(SpeedIndexByLocation); \ 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 e0da69b8b..45167c9b1 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/TimeToRender/TimeToRender.tsx @@ -27,7 +27,7 @@ function TimeToRender(props: Props) { const onSelect = (params) => { // const _params = { density: 70 } - console.log('params', params) // TODO reload the data with new params; + // TODO reload the data with new params; // this.props.fetchWidget(WIDGET_KEY, dashbaordStore.period, props.platform, { ..._params, url: params.value }) } diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 81d2ce430..13507960d 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -80,7 +80,6 @@ function WidgetForm(props: Props) { } const onObserveChanges = () => { - console.log('observe changes'); // metricStore.fetchMetricChartData(metric); } diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index 54982ba9c..9a514dd07 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -39,7 +39,6 @@ function WidgetSessions(props: Props) { useEffect(() => { widget.fetchSessions({ ...filter, filter: widget.toJsonDrilldown()}).then(res => { - console.log('res', res) setData(res); }); }, [filter.startTimestamp, filter.endTimestamp, filter.filters]); diff --git a/frontend/app/components/Session_/Performance/Performance.js b/frontend/app/components/Session_/Performance/Performance.js index 78aea13ce..3310970b3 100644 --- a/frontend/app/components/Session_/Performance/Performance.js +++ b/frontend/app/components/Session_/Performance/Performance.js @@ -129,7 +129,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/components/Session_/Player/Player.js b/frontend/app/components/Session_/Player/Player.js index 27875df52..7391c8992 100644 --- a/frontend/app/components/Session_/Player/Player.js +++ b/frontend/app/components/Session_/Player/Player.js @@ -49,8 +49,6 @@ export default class Player extends React.PureComponent { closedLive, } = this.props; - console.log('PlayerControls', PlayerControls) - return (
diff --git a/frontend/app/duck/sessions.js b/frontend/app/duck/sessions.js index f8dc4020c..47b960bc2 100644 --- a/frontend/app/duck/sessions.js +++ b/frontend/app/duck/sessions.js @@ -298,7 +298,6 @@ export function fetchErrorStackList(sessionId, errorId) { } export const fetch = (sessionId, isLive = false) => (dispatch, getState) => { - console.log('isLive', isLive) dispatch({ types: FETCH.toArray(), call: client => client.get(isLive ? `/assist/sessions/${ sessionId }` : `/sessions2/${ sessionId }`), diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index 158644f68..89cf153a0 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -76,7 +76,6 @@ export default class MetricStore implements IMetricStore { reaction( () => this.metricsSearch, (metricsSearch) => { // TODO filter the list for View - console.log('metricsSearch', metricsSearch) this.page = 1 this.paginatedList } diff --git a/frontend/app/mstore/types/filterItem.ts b/frontend/app/mstore/types/filterItem.ts index 7b7e4999a..64fe46a6d 100644 --- a/frontend/app/mstore/types/filterItem.ts +++ b/frontend/app/mstore/types/filterItem.ts @@ -36,7 +36,6 @@ export default class FilterItem { } fromJson(json, mainFilterKey = '') { - console.log('fromJson', json.type) let _filter = filtersMap[json.type] || {} if (mainFilterKey) { const mainFilter = filtersMap[mainFilterKey]; diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index adff4e390..e67fdf4ed 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -200,7 +200,6 @@ export default class Widget implements IWidget { fetchSessions(filter: any): Promise { this.sessionsLoading = true return new Promise((resolve, reject) => { - console.log('fetching sessions', filter) metricService.fetchSessions(this.metricId, filter).then(response => { resolve(response.map(cat => { return { From 60b5ad27ab047e32db245472bee32d40b709fe8e Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 15:51:03 +0200 Subject: [PATCH 200/294] feat(UI): update version number --- frontend/env.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/env.js b/frontend/env.js index 8c5294fd8..df0044b23 100644 --- a/frontend/env.js +++ b/frontend/env.js @@ -13,7 +13,7 @@ const oss = { ORIGIN: () => 'window.location.origin', API_EDP: () => 'window.location.origin + "/api"', ASSETS_HOST: () => 'window.location.origin + "/assets"', - VERSION: '1.5.4', + VERSION: '1.5.5', SOURCEMAP: true, MINIO_ENDPOINT: process.env.MINIO_ENDPOINT, MINIO_PORT: process.env.MINIO_PORT, From 11f368ed7af85fe4dddac05342d9150e43092998 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 16:07:35 +0200 Subject: [PATCH 201/294] feat(api): dashboards append widgets on update --- api/chalicelib/core/dashboards2.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 38fd2bcdf..f46c5a8ca 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -125,6 +125,13 @@ 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 = """SELECT COALESCE(COUNT(*),0) AS count + FROM dashboard_widgets + WHERE dashboard_id = %(dashboard_id)s;""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()} + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + offset = row["count"] pg_query = f"""UPDATE dashboards SET name = %(name)s {", is_public = %(is_public)s" if data.is_public is not None else ""} @@ -132,7 +139,6 @@ def update_dashboard(project_id, user_id, dashboard_id, data: schemas.EditDashbo 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, **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) @@ -143,7 +149,7 @@ def update_dashboard(project_id, user_id, dashboard_id, data: schemas.EditDashbo # .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}) + params[f"config_{i}"] = json.dumps({"position": i + offset}) cur.execute(cur.mogrify(pg_query, params)) From a33034e273cc7bfe74816c40d61e0adc3f6cd717 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 16:25:40 +0200 Subject: [PATCH 202/294] fix(ui) - filter options --- .../ResourceLoadingTime/ResourceLoadingTime.tsx | 2 +- frontend/app/mstore/types/filterItem.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx index 63d63d207..905c347f2 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResourceLoadingTime/ResourceLoadingTime.tsx @@ -78,7 +78,7 @@ function ResourceLoadingTime(props: Props) { /> */}
- + new FilterItem().fromJson(i, json.type)) : [] return this } From 9f23d12e04c5ec22b23d155f0d9ec37d6845314d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 16:27:30 +0200 Subject: [PATCH 203/294] feat(api): changed default value for source search --- api/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/schemas.py b/api/schemas.py index a0eec022b..f1daef481 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -588,7 +588,7 @@ class SessionSearchFilterSchema(__MixedSearchFilter): Optional[List[Union[IssueType, PlatformType, int, str]]]] = Field(...) type: FilterType = Field(...) operator: Union[SearchEventOperator, MathOperator] = Field(...) - source: Optional[Union[ErrorSource, str]] = Field(default=ErrorSource.js_exception) + source: Optional[Union[ErrorSource, str]] = Field(default=None) @root_validator def filter_validator(cls, values): From 92e383b29a18e618b96f4e397c7929905e15b394 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 15 Apr 2022 16:27:30 +0200 Subject: [PATCH 204/294] feat(api): changed default value for source search --- api/schemas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/schemas.py b/api/schemas.py index a0eec022b..f1daef481 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -588,7 +588,7 @@ class SessionSearchFilterSchema(__MixedSearchFilter): Optional[List[Union[IssueType, PlatformType, int, str]]]] = Field(...) type: FilterType = Field(...) operator: Union[SearchEventOperator, MathOperator] = Field(...) - source: Optional[Union[ErrorSource, str]] = Field(default=ErrorSource.js_exception) + source: Optional[Union[ErrorSource, str]] = Field(default=None) @root_validator def filter_validator(cls, values): From 4a5ac0012b086adddc6c8be7a092173f900e507f Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 16:28:30 +0200 Subject: [PATCH 205/294] fix(ui) - removed logs --- frontend/app/mstore/types/filterItem.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/app/mstore/types/filterItem.ts b/frontend/app/mstore/types/filterItem.ts index b46244a93..7678f4620 100644 --- a/frontend/app/mstore/types/filterItem.ts +++ b/frontend/app/mstore/types/filterItem.ts @@ -37,9 +37,7 @@ export default class FilterItem { } fromJson(json, mainFilterKey = '') { - console.log('json', json) let _filter = filtersMap[json.type] || {} - console.log('_filter', _filter) if (mainFilterKey) { const mainFilter = filtersMap[mainFilterKey]; const subFilterMap = {} From 5a94e3c23f650d3e117d5f81fc3eda4d9a476aa8 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 16:31:53 +0200 Subject: [PATCH 206/294] fix(ui) - browser bar fix --- .../SessionsPerBrowser/Bar.css | 20 +++++++++++ .../SessionsPerBrowser/Bar.js | 34 +++++++++++++++++++ .../SessionsPerBrowser/SessionsPerBrowser.tsx | 3 +- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/Bar.css create mode 100644 frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/Bar.js diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/Bar.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/Bar.css new file mode 100644 index 000000000..dde6009e4 --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/Bar.css @@ -0,0 +1,20 @@ +.bar { + height: 5px; + width: 100%; + border-radius: 3px; + display: flex; + align-items: center; + & div { + padding: 0 5px; + height: 20px; + color: #FFF; + } + & div:first-child { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + } + & div:last-child { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + } +} \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/Bar.js b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/Bar.js new file mode 100644 index 000000000..b1204ee9e --- /dev/null +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/Bar.js @@ -0,0 +1,34 @@ +import React from 'react' +import stl from './Bar.css' +// import { Styles } from '../common' +import { TextEllipsis } from 'UI'; + +const Bar = ({ className = '', versions = [], width = 0, avg, domain, colors }) => { + return ( +
+
+
+ {versions.map((v, i) => { + const w = (v.value * 100)/ avg; + return ( +
+ +
Version: {v.key}
+
Sessions: {v.value}
+
+ } /> +
+ ) + })} +
+
+ {`${avg}`} +
+
+
{domain}
+
+ ) +} + +export default Bar \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/SessionsPerBrowser.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/SessionsPerBrowser.tsx index 5fd2c8d48..ad8663390 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/SessionsPerBrowser.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SessionsPerBrowser/SessionsPerBrowser.tsx @@ -1,8 +1,7 @@ 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'; +import Bar from './Bar'; interface Props { data: any From 4ce781f6e20671cf7eb98727a1d459ef64c84592 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Fri, 15 Apr 2022 19:51:54 +0200 Subject: [PATCH 207/294] fix(vars): removed wrong values Signed-off-by: rjshrjndrn --- scripts/helmcharts/vars.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/helmcharts/vars.yaml b/scripts/helmcharts/vars.yaml index 49d48525b..0cb1399c6 100644 --- a/scripts/helmcharts/vars.yaml +++ b/scripts/helmcharts/vars.yaml @@ -78,7 +78,7 @@ global: # if you're using one node installation, where # you're using local s3, make sure these variables # are same as minio.global.minio.accesskey and secretKey - accessKey: "YkkPAPYjogRlicqvCuNSHkfsdGtCCq" + accessKey: "changeMeMinioAccessKey" secretKey: "changeMeMinioPassword" email: emailHost: '' @@ -92,7 +92,7 @@ global: emailFrom: 'OpenReplay' enterpriseEditionLicense: "" - domainName: "foss.openreplay.com" + domainName: "" # If there is multiple nodes in the kubernetes cluster, # we'll have to create a NFS share PVC for both the containers to share data. From f116bcf529ea039e770430a8d32ba96651a99d35 Mon Sep 17 00:00:00 2001 From: rjshrjndrn Date: Fri, 15 Apr 2022 20:07:29 +0200 Subject: [PATCH 208/294] chore(helm): remove duplicated value Signed-off-by: rjshrjndrn --- .../openreplay/charts/storage/templates/deployment.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/helmcharts/openreplay/charts/storage/templates/deployment.yaml b/scripts/helmcharts/openreplay/charts/storage/templates/deployment.yaml index 0004bfc7f..b36c06c94 100644 --- a/scripts/helmcharts/openreplay/charts/storage/templates/deployment.yaml +++ b/scripts/helmcharts/openreplay/charts/storage/templates/deployment.yaml @@ -52,8 +52,6 @@ spec: value: {{ .Values.global.s3.recordingsBucket }} - name: S3_BUCKET_IOS value: {{ .Values.global.s3.recordingsBucket }} - - name: REDIS_STRING - value: '{{ .Values.global.redis.redisHost }}:{{ .Values.global.redis.redisPort }}' - name: LICENSE_KEY value: '{{ .Values.global.enterpriseEditionLicense }}' - name: REDIS_STRING From e5985f376eec4a591c36554ba7c78de74cd3241d Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 15 Apr 2022 20:09:31 +0200 Subject: [PATCH 209/294] feat(tracker): 3.5.6: use fetch-keepalive; webworker refactor; isInstance fix --- tracker/tracker/package.json | 2 +- tracker/tracker/src/main/app/context.ts | 6 +- tracker/tracker/src/main/app/index.ts | 34 +-- tracker/tracker/src/main/app/session.ts | 1 - tracker/tracker/src/main/index.ts | 4 +- tracker/tracker/src/messages/webworker.ts | 17 -- tracker/tracker/src/webworker/BatchWriter.ts | 71 ++++++ tracker/tracker/src/webworker/QueueSender.ts | 84 +++++++ tracker/tracker/src/webworker/index.ts | 231 +++++++------------ tracker/tracker/src/webworker/types.ts | 19 ++ 10 files changed, 282 insertions(+), 187 deletions(-) delete mode 100644 tracker/tracker/src/messages/webworker.ts create mode 100644 tracker/tracker/src/webworker/BatchWriter.ts create mode 100644 tracker/tracker/src/webworker/QueueSender.ts create mode 100644 tracker/tracker/src/webworker/types.ts diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 9c7920ad7..089f95f24 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "3.5.5", + "version": "3.5.6", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/main/app/context.ts b/tracker/tracker/src/main/app/context.ts index fd7ec11dd..87e4df20a 100644 --- a/tracker/tracker/src/main/app/context.ts +++ b/tracker/tracker/src/main/app/context.ts @@ -32,6 +32,8 @@ type Constructor = { new (...args: any[]): T , name: string }; // TODO: we need a type expert here so we won't have to ignore the lines // TODO: use it everywhere (static function; export from which file? <-- global Window typing required) + // TODO: most efficient and common way + // Problem: on YouTube there is context[constr.name] undefined for constr=ShadowDom due to some minimisations export function isInstance(node: Node, constr: Constructor): node is T { const doc = node.ownerDocument; if (!doc) { // null if Document @@ -43,14 +45,14 @@ export function isInstance(node: Node, constr: Cons doc.defaultView; // TODO: smart global typing for Window object while(context !== window) { // @ts-ignore - if (node instanceof context[constr.name]) { + if (context[constr.name] && node instanceof context[constr.name]) { return true } // @ts-ignore context = context.parent || window } // @ts-ignore - return node instanceof context[constr.name] + return context[constr.name] ? node instanceof context[constr.name] : node instanceof constr } // TODO: ensure 1. it works in every cases (iframes/detached nodes) and 2. the most efficient diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts index de9518af6..8fd2cb4ac 100644 --- a/tracker/tracker/src/main/app/index.ts +++ b/tracker/tracker/src/main/app/index.ts @@ -15,7 +15,7 @@ import type { Options as SanitizerOptions } from "./sanitizer.js"; import type { Options as LoggerOptions } from "./logger.js" -import type { Options as WebworkerOptions, WorkerMessageData } from "../../messages/webworker.js"; +import type { Options as WebworkerOptions, WorkerMessageData } from "../../webworker/types.js"; export interface OnStartInfo { sessionID: string, @@ -23,10 +23,12 @@ export interface OnStartInfo { userUUID: string, } + +// TODO: Unify and clearly describe options logic export interface StartOptions { userID?: string, metadata?: Record, - forceNew: boolean, + forceNew?: boolean, } type AppOptions = { @@ -46,12 +48,12 @@ type AppOptions = { // @deprecated onStart?: (info: OnStartInfo) => void; -} & WebworkerOptions; +} & WebworkerOptions; export type Options = AppOptions & ObserverOptions & SanitizerOptions -type Callback = () => void; -type CommitCallback = (messages: Array) => void; +type Callback = () => void +type CommitCallback = (messages: Array) => void enum ActivityState { NotActive, Starting, @@ -130,13 +132,13 @@ export default class App { this._debug("webworker_error", e) } this.worker.onmessage = ({ data }: MessageEvent) => { - if (data === null) { + if (data === "failed") { this.stop(); } else if (data === "restart") { this.stop(); this.start({ forceNew: true }); } - }; + } const alertWorker = () => { if (this.worker) { this.worker.postMessage(null); @@ -331,14 +333,15 @@ export default class App { sessionStorage.setItem(this.options.session_pageno_key, pageNo.toString()); const startInfo = this.getStartInfo() - const messageData: WorkerMessageData = { - ingestPoint: this.options.ingestPoint, + const startWorkerMsg: WorkerMessageData = { + type: "start", pageNo, - startTimestamp: startInfo.timestamp, + ingestPoint: this.options.ingestPoint, + timestamp: startInfo.timestamp, connAttemptCount: this.options.connAttemptCount, connAttemptGap: this.options.connAttemptGap, } - this.worker.postMessage(messageData); // brings delay of 10th ms? + this.worker.postMessage(startWorkerMsg) // brings delay of 10th ms? const sReset = sessionStorage.getItem(this.options.session_reset_key); sessionStorage.removeItem(this.options.session_reset_key); @@ -385,7 +388,12 @@ export default class App { }); this.activityState = ActivityState.Active - this.worker.postMessage({ token, beaconSizeLimit }); + const startWorkerMsg: WorkerMessageData = { + type: "auth", + token, + beaconSizeLimit + } + this.worker.postMessage(startWorkerMsg) this.startCallbacks.forEach((cb) => cb()); this.observer.observe(); this.ticker.start(); @@ -411,7 +419,7 @@ export default class App { }) } - start(options: StartOptions = { forceNew: false }): Promise { + start(options: StartOptions = {}): Promise { if (!document.hidden) { return this._start(options); } else { diff --git a/tracker/tracker/src/main/app/session.ts b/tracker/tracker/src/main/app/session.ts index ec5dadf51..169241aa7 100644 --- a/tracker/tracker/src/main/app/session.ts +++ b/tracker/tracker/src/main/app/session.ts @@ -78,7 +78,6 @@ export default class Session { sessionID: this.sessionID, metadata: this.metadata, userID: this.userID, - } } } \ No newline at end of file diff --git a/tracker/tracker/src/main/index.ts b/tracker/tracker/src/main/index.ts index 8961d6e54..a55df1d1c 100644 --- a/tracker/tracker/src/main/index.ts +++ b/tracker/tracker/src/main/index.ts @@ -151,7 +151,7 @@ export default class API { return this.app.active(); } - start(startOpts?: StartOptions) : Promise { + start(startOpts?: Partial) : Promise { if (!IN_BROWSER) { console.error(`OpenReplay: you are trying to start Tracker on a node.js environment. If you want to use OpenReplay with SSR, please, use componentDidMount or useEffect API for placing the \`tracker.start()\` line. Check documentation on ${DOCS_HOST}${DOCS_SETUP}`) return Promise.reject("Trying to start not in browser."); @@ -159,7 +159,7 @@ export default class API { if (this.app === null) { return Promise.reject("Browser doesn't support required api, or doNotTrack is active."); } - // TODO: check argument typing + // TODO: check argument type return this.app.start(startOpts); } stop(): void { diff --git a/tracker/tracker/src/messages/webworker.ts b/tracker/tracker/src/messages/webworker.ts deleted file mode 100644 index e4bc97818..000000000 --- a/tracker/tracker/src/messages/webworker.ts +++ /dev/null @@ -1,17 +0,0 @@ -// TODO: "common" folder instead of "messages". (better file structure) -export interface Options { - connAttemptCount?: number; - connAttemptGap?: number; - beaconSize?: number; -} - -type Settings = { - ingestPoint?: string; - token?: string; - pageNo?: number; - startTimestamp?: number; - timeAdjustment?: number; - beaconSizeLimit?: number; -} & Partial; - -export type WorkerMessageData = null | "stop" | Settings | Array<{ _id: number }>; \ No newline at end of file diff --git a/tracker/tracker/src/webworker/BatchWriter.ts b/tracker/tracker/src/webworker/BatchWriter.ts new file mode 100644 index 000000000..de0e956ef --- /dev/null +++ b/tracker/tracker/src/webworker/BatchWriter.ts @@ -0,0 +1,71 @@ +import Writer from "../messages/writer.js"; +import Message from "../messages/message.js"; +import { + BatchMeta, + Timestamp, +} from "../messages/index.js"; + +export default class BatchWriter { + private nextIndex = 0 + private beaconSize = 2 * 1e5 // Default 200kB + private writer = new Writer(this.beaconSize) + private isEmpty = true + + constructor( + private readonly pageNo: number, + private timestamp: number, + private onBatch: (batch: Uint8Array) => void + ) { + this.prepareBatchMeta() + } + + private prepareBatchMeta(): boolean { + return new BatchMeta(this.pageNo, this.nextIndex, this.timestamp).encode(this.writer) + } + + private beaconSizeLimit = 1e6 + setBeaconSizeLimit(limit: number) { + this.beaconSizeLimit = limit + } + + writeMessage(message: Message) { + if (message instanceof Timestamp) { + this.timestamp = (message).timestamp; + } + + if (!message.encode(this.writer)) { + if (!this.isEmpty) { + this.onBatch(this.writer.flush()) + this.prepareBatchMeta() + } + + while (!message.encode(this.writer)) { + if (this.beaconSize === this.beaconSizeLimit) { + console.warn("OpenReplay: beacon size overflow. Skipping large message."); + this.writer.reset() + this.prepareBatchMeta() + this.isEmpty = true + return + } + // MBTODO: tempWriter for one message? + this.beaconSize = Math.min(this.beaconSize*2, this.beaconSizeLimit) + this.writer = new Writer(this.beaconSize) + this.prepareBatchMeta() + } + } + this.writer.checkpoint() + this.nextIndex++ + this.isEmpty = false + } + + flush(): Uint8Array | null { + if (this.isEmpty) { return null } + this.isEmpty = true + return this.writer.flush() + } + + clean() { + this.writer.reset() + } + +} \ No newline at end of file diff --git a/tracker/tracker/src/webworker/QueueSender.ts b/tracker/tracker/src/webworker/QueueSender.ts new file mode 100644 index 000000000..b2cb4d3eb --- /dev/null +++ b/tracker/tracker/src/webworker/QueueSender.ts @@ -0,0 +1,84 @@ +const INGEST_PATH = "/v1/web/i" + +export default class QueueSender { + private attemptsCount = 0 + private busy = false + private readonly queue: Array = [] + private readonly ingestURL + private token: string | null = null + constructor( + ingestBaseURL: string, + private readonly onUnauthorised: Function, + private readonly onFailure: Function, + private readonly MAX_ATTEMPTS_COUNT = 10, + private readonly ATTEMPT_TIMEOUT = 1000, + ) { + this.ingestURL = ingestBaseURL + INGEST_PATH + } + + authorise(token: string) { + this.token = token + } + + push(batch: Uint8Array) { + if (this.busy || !this.token) { + this.queue.push(batch) + } else { + this.busy = true + this.sendBatch(batch) + } + } + + private retry(batch: Uint8Array) { + if (this.attemptsCount >= this.MAX_ATTEMPTS_COUNT) { + this.onFailure() + return + } + this.attemptsCount++ + setTimeout(() => this.sendBatch(batch), this.ATTEMPT_TIMEOUT * this.attemptsCount) + } + + // would be nice to use Beacon API, but it is not available in WebWorker + private sendBatch(batch: Uint8Array):void { + fetch(this.ingestURL, { + body: batch, + method: 'POST', + headers: { + "Authorization": "Bearer " + this.token, + //"Content-Type": "", + }, + keepalive: true, + }) + .then(r => { + if (r.status === 401) { // TODO: continuous session ? + this.busy = false + this.onUnauthorised() + return + } else if (r.status >= 400) { + this.retry(batch) + return + } + + // Success + this.attemptsCount = 0 + const nextBatch = this.queue.shift() + if (nextBatch) { + this.sendBatch(nextBatch) + } else { + this.busy = false + } + }) + .catch(e => { + this.retry(batch) + }) // Does it handle offline exceptions (?) + + } + + clean() { + this.queue.length = 0 + } + +} + + + diff --git a/tracker/tracker/src/webworker/index.ts b/tracker/tracker/src/webworker/index.ts index f598ac0a4..519deeb0f 100644 --- a/tracker/tracker/src/webworker/index.ts +++ b/tracker/tracker/src/webworker/index.ts @@ -1,178 +1,107 @@ -import { classes, BatchMeta, Timestamp, SetPageVisibility, CreateDocument } from "../messages/index.js"; import Message from "../messages/message.js"; -import Writer from "../messages/writer.js"; +import { + classes, + SetPageVisibility, +} from "../messages/index.js"; +import QueueSender from "./QueueSender.js"; +import BatchWriter from "./BatchWriter.js"; -import type { WorkerMessageData } from "../messages/webworker.js"; +import type { WorkerMessageData } from "./types.js"; -const SEND_INTERVAL = 10 * 1000; -let BEACON_SIZE_LIMIT = 1e6 // Limit is set in the backend/services/http -let beaconSize = 2 * 1e5; // Default 400kB +const AUTO_SEND_INTERVAL = 10 * 1000 - -let writer: Writer = new Writer(beaconSize); - -let ingestPoint: string = ""; -let token: string = ""; -let pageNo: number = 0; -let timestamp: number = 0; -let timeAdjustment: number = 0; -let nextIndex: number = 0; -// TODO: clear logic: isEmpty here means presence of BatchMeta but absence of other messages -// BatchWriter should be abstracted -let isEmpty: boolean = true; - -function writeBatchMeta(): boolean { // TODO: move to encoder - return new BatchMeta(pageNo, nextIndex, timestamp).encode(writer) -} - -let sendIntervalID: ReturnType | null = null; - -const sendQueue: Array = []; -let busy = false; -let attemptsCount = 0; -let ATTEMPT_TIMEOUT = 3000; -let MAX_ATTEMPTS_COUNT = 10; - -// TODO?: exploit https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon -function sendBatch(batch: Uint8Array):void { - const xhr = new XMLHttpRequest(); - // TODO: async=false (3d param) instead of sendQueue array ? - xhr.open("POST", ingestPoint + "/v1/web/i", false); - xhr.setRequestHeader("Authorization", "Bearer " + token); - // xhr.setRequestHeader("Content-Type", ""); - - function retry() { - if (attemptsCount >= MAX_ATTEMPTS_COUNT) { - reset(); - self.postMessage(null); - return - } - attemptsCount++; - setTimeout(() => sendBatch(batch), ATTEMPT_TIMEOUT); - } - xhr.onreadystatechange = function() { - if (this.readyState === 4) { - if (this.status == 0) { - return; // happens simultaneously with onerror TODO: clear codeflow - } - if (this.status === 401) { // Unauthorised (Token expired) - busy = false - self.postMessage("restart") - return - } else if (this.status >= 400) { // TODO: test workflow. After 400+ it calls /start for some reason - retry() - return - } - // Success - attemptsCount = 0 - const nextBatch = sendQueue.shift(); - if (nextBatch) { - sendBatch(nextBatch); - } else { - busy = false; - } - } - }; - xhr.onerror = retry // TODO: when in Offline mode it doesn't handle the error - // TODO: handle offline exception (?) - xhr.send(batch.buffer); -} +let sender: QueueSender | null = null +let writer: BatchWriter | null = null function send(): void { - if (isEmpty || token === "" || ingestPoint === "") { - return; + if (!sender || !writer) { + return } - const batch = writer.flush(); - if (busy) { - sendQueue.push(batch); - } else { - busy = true; - sendBatch(batch); - } - isEmpty = true; - writeBatchMeta(); + const batch = writer.flush() + batch && sender.push(batch) } + function reset() { - ingestPoint = "" - token = "" if (sendIntervalID !== null) { clearInterval(sendIntervalID); sendIntervalID = null; } - sendQueue.length = 0; - writer.reset(); + if (writer) { + writer.clean() + writer = null + } } -let restartTimeoutID: ReturnType; - -function hasTimestamp(msg: any): msg is { timestamp: number } { - return typeof msg === 'object' && typeof msg.timestamp === 'number'; +function resetCleanQueue() { + if (sender) { + sender.clean() + sender = null + } + reset() } +let sendIntervalID: ReturnType | null = null +let restartTimeoutID: ReturnType + self.onmessage = ({ data }: MessageEvent) => { - if (data === null) { - send(); - return; + if (data == null) { + send() // TODO: sendAll? + return } + if (data === "stop") { - send(); - reset(); - return; + send() + reset() + return } - if (!Array.isArray(data)) { - ingestPoint = data.ingestPoint || ingestPoint; - token = data.token || token; - pageNo = data.pageNo || pageNo; - timestamp = data.startTimestamp || timestamp; - timeAdjustment = data.timeAdjustment || timeAdjustment; - MAX_ATTEMPTS_COUNT = data.connAttemptCount || MAX_ATTEMPTS_COUNT; - ATTEMPT_TIMEOUT = data.connAttemptGap || ATTEMPT_TIMEOUT; - BEACON_SIZE_LIMIT = data.beaconSizeLimit || BEACON_SIZE_LIMIT; - beaconSize = Math.min(BEACON_SIZE_LIMIT, data.beaconSize || beaconSize); - if (writer.isEmpty()) { - writeBatchMeta(); - } - if (sendIntervalID === null) { - sendIntervalID = setInterval(send, SEND_INTERVAL); - } - return; - } - data.forEach((data) => { - const message: Message = new (classes.get(data._id))(); - Object.assign(message, data); - if (message instanceof Timestamp) { - timestamp = (message).timestamp; - } else if (message instanceof SetPageVisibility) { - if ( (message).hidden) { - restartTimeoutID = setTimeout(() => self.postMessage("restart"), 30*60*1000); - } else { - clearTimeout(restartTimeoutID); - } - } - - writer.checkpoint(); // TODO: encapsulate in writer - if (!message.encode(writer)) { - send(); - // writer.reset(); // TODO: semantically clear code - if (!message.encode(writer)) { // Try to encode within empty state - // MBTODO: tempWriter for one message? - while (!message.encode(writer)) { - if (beaconSize === BEACON_SIZE_LIMIT) { - console.warn("OpenReplay: beacon size overflow."); - writer.reset(); - writeBatchMeta(); - return - } - beaconSize = Math.min(beaconSize*2, BEACON_SIZE_LIMIT); - writer = new Writer(beaconSize); - writeBatchMeta(); + if (Array.isArray(data)) { + // Message[] + data.forEach((data) => { + const message: Message = new (classes.get(data._id))(); + Object.assign(message, data) + if (message instanceof SetPageVisibility) { + if ( (message).hidden) { + restartTimeoutID = setTimeout(() => self.postMessage("restart"), 30*60*1000) + } else { + clearTimeout(restartTimeoutID) } } - }; - nextIndex++; // TODO: encapsulate in writer - isEmpty = false; - }); + writer && writer.writeMessage(message) + }) + return + } + + if (data.type === 'start') { + sender = new QueueSender( + data.ingestPoint, + () => { // onUnauthorised + self.postMessage("restart") + }, + () => { // onFailure + resetCleanQueue() + self.postMessage("failed") + }, + data.connAttemptCount, + data.connAttemptGap, + ) + writer = new BatchWriter( + data.pageNo, + data.timestamp, + // onBatch + batch => sender && sender.push(batch) + ) + if (sendIntervalID === null) { + sendIntervalID = setInterval(send, AUTO_SEND_INTERVAL) + } + return + } + + if (data.type === "auth") { + sender && sender.authorise(data.token) + data.beaconSizeLimit && writer && writer.setBeaconSizeLimit(data.beaconSizeLimit) + return + } }; diff --git a/tracker/tracker/src/webworker/types.ts b/tracker/tracker/src/webworker/types.ts new file mode 100644 index 000000000..34ac7f582 --- /dev/null +++ b/tracker/tracker/src/webworker/types.ts @@ -0,0 +1,19 @@ +export interface Options { + connAttemptCount?: number + connAttemptGap?: number +} + +type Start = { + type: "start", + ingestPoint: string + pageNo: number + timestamp: number +} & Options + +type Auth = { + type: "auth" + token: string + beaconSizeLimit?: number +} + +export type WorkerMessageData = null | "stop" | Start | Auth | Array<{ _id: number }> \ No newline at end of file From fd85745caef337059ad58a9401ba78533a58b46d Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 15 Apr 2022 20:15:34 +0200 Subject: [PATCH 210/294] fix(tracker-assist): 3.5.8: fix fully specified --- tracker/tracker-assist/package.json | 2 +- tracker/tracker-assist/src/CallWindow.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tracker/tracker-assist/package.json b/tracker/tracker-assist/package.json index b0152f0db..19149b62a 100644 --- a/tracker/tracker-assist/package.json +++ b/tracker/tracker-assist/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-assist", "description": "Tracker plugin for screen assistance through the WebRTC", - "version": "3.5.7", + "version": "3.5.8", "keywords": [ "WebRTC", "assistance", diff --git a/tracker/tracker-assist/src/CallWindow.ts b/tracker/tracker-assist/src/CallWindow.ts index f8c852814..ae2bdd3fa 100644 --- a/tracker/tracker-assist/src/CallWindow.ts +++ b/tracker/tracker-assist/src/CallWindow.ts @@ -1,5 +1,5 @@ import type { LocalStream } from './LocalStream.js'; -import attachDND from './dnd'; +import attachDND from './dnd.js'; const SS_START_TS_KEY = "__openreplay_assist_call_start_ts" From c9b1813e6a8cc41d6a985a1558cc111f12ec9104 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 15 Apr 2022 20:31:01 +0200 Subject: [PATCH 211/294] feat/codefix(ee/backend): MAXPOL env & remove alerts --- .../{messages_ios.go => messages-ios.go} | 0 .../{messages_web.go => messages-web.go} | 0 ee/backend/pkg/db/postgres/alert.go | 228 ------------------ ee/backend/pkg/kafka/consumer.go | 1 + 4 files changed, 1 insertion(+), 228 deletions(-) rename ee/backend/pkg/db/clickhouse/{messages_ios.go => messages-ios.go} (100%) rename ee/backend/pkg/db/clickhouse/{messages_web.go => messages-web.go} (100%) delete mode 100644 ee/backend/pkg/db/postgres/alert.go diff --git a/ee/backend/pkg/db/clickhouse/messages_ios.go b/ee/backend/pkg/db/clickhouse/messages-ios.go similarity index 100% rename from ee/backend/pkg/db/clickhouse/messages_ios.go rename to ee/backend/pkg/db/clickhouse/messages-ios.go diff --git a/ee/backend/pkg/db/clickhouse/messages_web.go b/ee/backend/pkg/db/clickhouse/messages-web.go similarity index 100% rename from ee/backend/pkg/db/clickhouse/messages_web.go rename to ee/backend/pkg/db/clickhouse/messages-web.go diff --git a/ee/backend/pkg/db/postgres/alert.go b/ee/backend/pkg/db/postgres/alert.go deleted file mode 100644 index 301d7b540..000000000 --- a/ee/backend/pkg/db/postgres/alert.go +++ /dev/null @@ -1,228 +0,0 @@ -package postgres - -import ( - "database/sql" - "errors" - "fmt" - sq "github.com/Masterminds/squirrel" - "log" - "strconv" - "time" -) - -type TimeString sql.NullString -type query struct { - Left string `db:"query.left" json:"left"` - Operator string `db:"query.operator" json:"operator"` - Right float64 `db:"query.right" json:"right"` -} -type options struct { - RenotifyInterval int64 `db:"options.renotifyInterval" json:"renotifyInterval"` - LastNotification int64 `db:"options.lastNotification" json:"lastNotification;omitempty"` - CurrentPeriod int64 `db:"options.currentPeriod" json:"currentPeriod"` - PreviousPeriod int64 `db:"options.previousPeriod" json:"previousPeriod;omitempty"` - Message []map[string]string `db:"options.message" json:"message;omitempty"` - Change string `db:"options.change" json:"change;omitempty"` -} -type Alert struct { - AlertID uint32 `db:"alert_id" json:"alert_id"` - ProjectID uint32 `db:"project_id" json:"project_id"` - Name string `db:"name" json:"name"` - Description sql.NullString `db:"description" json:"description"` - Active bool `db:"active" json:"active"` - DetectionMethod string `db:"detection_method" json:"detection_method"` - Query query `db:"query" json:"query"` - DeletedAt *int64 `db:"deleted_at" json:"deleted_at"` - CreatedAt *int64 `db:"created_at" json:"created_at"` - Options options `db:"options" json:"options"` - TenantId uint32 `db:"tenant_id" json:"tenant_id"` -} - -func (pg *Conn) IterateAlerts(iter func(alert *Alert, err error)) error { - rows, err := pg.query(` - SELECT - alerts.alert_id, - alerts.project_id, - alerts.name, - alerts.description, - alerts.active, - alerts.detection_method, - alerts.query, - CAST(EXTRACT(epoch FROM alerts.deleted_at) * 1000 AS BIGINT) AS deleted_at, - CAST(EXTRACT(epoch FROM alerts.created_at) * 1000 AS BIGINT) AS created_at, - alerts.options, - projects.tenant_id - FROM public.alerts INNER JOIN public.projects USING(project_id) - WHERE alerts.active AND alerts.deleted_at ISNULL; - `) - if err != nil { - return err - } - defer rows.Close() - for rows.Next() { - a := new(Alert) - if err = rows.Scan( - &a.AlertID, - &a.ProjectID, - &a.Name, - &a.Description, - &a.Active, - &a.DetectionMethod, - &a.Query, - &a.DeletedAt, - &a.CreatedAt, - &a.Options, - &a.TenantId, - ); err != nil { - iter(nil, err) - continue - } - iter(a, nil) - } - - if err = rows.Err(); err != nil { - return err - } - return nil -} - -func (pg *Conn) SaveLastNotification(allIds []uint32) error { - var paramrefs string - for _, v := range allIds { - paramrefs += strconv.Itoa(int(v)) + `,` - } - paramrefs = paramrefs[:len(paramrefs)-1] // remove last "," - q := "UPDATE public.Alerts SET options = options||'{\"lastNotification\":" + strconv.Itoa(int(time.Now().Unix()*1000)) + "}'::jsonb WHERE alert_id IN (" + paramrefs + ");" - //log.Println(q) - log.Println("Updating PG") - return pg.exec(q) -} - -type columnDefinition struct { - table string - formula string - condition string - group string -} - -var LeftToDb = map[string]columnDefinition{ - "performance.dom_content_loaded.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "COALESCE(AVG(NULLIF(dom_content_loaded_time ,0)),0)"}, - "performance.first_meaningful_paint.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "COALESCE(AVG(NULLIF(first_contentful_paint_time,0)),0)"}, - "performance.page_load_time.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(load_time ,0))"}, - "performance.dom_build_time.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(dom_building_time,0))"}, - "performance.speed_index.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(speed_index,0))"}, - "performance.page_response_time.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(response_time,0))"}, - "performance.ttfb.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(first_paint_time,0))"}, - "performance.time_to_render.average": {table: "events.pages INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(visually_complete,0))"}, - "performance.image_load_time.average": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(resources.duration,0))", condition: "type='img'"}, - "performance.request_load_time.average": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(resources.duration,0))", condition: "type='fetch'"}, - "resources.load_time.average": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "AVG(NULLIF(resources.duration,0))"}, - "resources.missing.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(DISTINCT url_hostpath)", condition: "success= FALSE"}, - "errors.4xx_5xx.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(session_id)", condition: "status/100!=2"}, - "errors.4xx.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(session_id)", condition: "status/100=4"}, - "errors.5xx.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(session_id)", condition: "status/100=5"}, - "errors.javascript.impacted_sessions.count": {table: "events.resources INNER JOIN public.sessions USING(session_id)", formula: "COUNT(DISTINCT session_id)", condition: "success= FALSE AND type='script'"}, - "performance.crashes.count": {table: "(SELECT *, start_ts AS timestamp FROM public.sessions WHERE errors_count > 0) AS sessions", formula: "COUNT(DISTINCT session_id)", condition: "errors_count > 0"}, - "errors.javascript.count": {table: "events.errors INNER JOIN public.errors AS m_errors USING (error_id)", formula: "COUNT(DISTINCT session_id)", condition: "source='js_exception'"}, - "errors.backend.count": {table: "events.errors INNER JOIN public.errors AS m_errors USING (error_id)", formula: "COUNT(DISTINCT session_id)", condition: "source!='js_exception'"}, -} - -//This is the frequency of execution for each threshold -var TimeInterval = map[int64]int64{ - 15: 3, - 30: 5, - 60: 10, - 120: 20, - 240: 30, - 1440: 60, -} - -func (a *Alert) CanCheck() bool { - now := time.Now().Unix() * 1000 - var repetitionBase int64 - - if repetitionBase = a.Options.CurrentPeriod; a.DetectionMethod == "change" && a.Options.CurrentPeriod > a.Options.PreviousPeriod { - repetitionBase = a.Options.PreviousPeriod - } - - if _, ok := TimeInterval[repetitionBase]; !ok { - log.Printf("repetitionBase: %d NOT FOUND", repetitionBase) - return false - } - return a.DeletedAt == nil && a.Active && - (a.Options.RenotifyInterval <= 0 || - a.Options.LastNotification <= 0 || - ((now - a.Options.LastNotification) > a.Options.RenotifyInterval*60*1000)) && - ((now-*a.CreatedAt)%(TimeInterval[repetitionBase]*60*1000)) < 60*1000 -} - -func (a *Alert) Build() (sq.SelectBuilder, error) { - colDef, ok := LeftToDb[a.Query.Left] - if !ok { - return sq.Select(), errors.New(fmt.Sprintf("!! unsupported metric '%s' from alert: %d:%s\n", a.Query.Left, a.AlertID, a.Name)) - } - - subQ := sq. - Select(colDef.formula + " AS value"). - From(colDef.table). - Where(sq.And{sq.Expr("project_id = $1 ", a.ProjectID), - sq.Expr(colDef.condition)}) - q := sq.Select(fmt.Sprint("value, coalesce(value,0)", a.Query.Operator, a.Query.Right, " AS valid")) - if len(colDef.group) > 0 { - subQ = subQ.Column(colDef.group + " AS group_value") - subQ = subQ.GroupBy(colDef.group) - q = q.Column("group_value") - } - - if a.DetectionMethod == "threshold" { - q = q.FromSelect(subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)), "stat") - } else if a.DetectionMethod == "change" { - if a.Options.Change == "change" { - if len(colDef.group) == 0 { - sub1, args1, _ := subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)).ToSql() - sub2, args2, _ := subQ.Where( - sq.And{ - sq.Expr("timestamp<$3 ", time.Now().Unix()-a.Options.CurrentPeriod*60), - sq.Expr("timestamp>=$4 ", time.Now().Unix()-2*a.Options.CurrentPeriod*60), - }).ToSql() - sub1, _, _ = sq.Expr("SELECT ((" + sub1 + ")-(" + sub2 + ")) AS value").ToSql() - q = q.JoinClause("FROM ("+sub1+") AS stat", append(args1, args2...)...) - } else { - subq1 := subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)) - sub2, args2, _ := subQ.Where( - sq.And{ - sq.Expr("timestamp<$3 ", time.Now().Unix()-a.Options.CurrentPeriod*60), - sq.Expr("timestamp>=$4 ", time.Now().Unix()-2*a.Options.CurrentPeriod*60), - }).ToSql() - sub1 := sq.Select("group_value", "(stat1.value-stat2.value) AS value").FromSelect(subq1, "stat1").JoinClause("INNER JOIN ("+sub2+") AS stat2 USING(group_value)", args2...) - q = q.FromSelect(sub1, "stat") - } - } else if a.Options.Change == "percent" { - if len(colDef.group) == 0 { - sub1, args1, _ := subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)).ToSql() - sub2, args2, _ := subQ.Where( - sq.And{ - sq.Expr("timestamp<$3 ", time.Now().Unix()-a.Options.CurrentPeriod*60), - sq.Expr("timestamp>=$4 ", time.Now().Unix()-a.Options.PreviousPeriod*60-a.Options.CurrentPeriod*60), - }).ToSql() - sub1, _, _ = sq.Expr("SELECT ((" + sub1 + ")/(" + sub2 + ")-1)*100 AS value").ToSql() - q = q.JoinClause("FROM ("+sub1+") AS stat", append(args1, args2...)...) - } else { - subq1 := subQ.Where(sq.Expr("timestamp>=$2 ", time.Now().Unix()-a.Options.CurrentPeriod*60)) - sub2, args2, _ := subQ.Where( - sq.And{ - sq.Expr("timestamp<$3 ", time.Now().Unix()-a.Options.CurrentPeriod*60), - sq.Expr("timestamp>=$4 ", time.Now().Unix()-a.Options.PreviousPeriod*60-a.Options.CurrentPeriod*60), - }).ToSql() - sub1 := sq.Select("group_value", "(stat1.value/stat2.value-1)*100 AS value").FromSelect(subq1, "stat1").JoinClause("INNER JOIN ("+sub2+") AS stat2 USING(group_value)", args2...) - q = q.FromSelect(sub1, "stat") - } - } else { - return q, errors.New("unsupported change method") - } - - } else { - return q, errors.New("unsupported detection method") - } - return q, nil -} \ No newline at end of file diff --git a/ee/backend/pkg/kafka/consumer.go b/ee/backend/pkg/kafka/consumer.go index cb3714316..65d2cd830 100644 --- a/ee/backend/pkg/kafka/consumer.go +++ b/ee/backend/pkg/kafka/consumer.go @@ -42,6 +42,7 @@ func NewConsumer( "enable.auto.commit": "false", "security.protocol": protocol, "go.application.rebalance.enable": true, + "max.poll.interval.ms": env.Int("KAFKA_MAX_POLL_INTERVAL_MS"), }) if err != nil { log.Fatalln(err) From 0b999aa7bfe675da7041218e1bc8f2716ecfc753 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 16:54:29 +0200 Subject: [PATCH 212/294] fix(ui) - widget remove fix id null --- .../Dashboard/components/WidgetWrapper/WidgetWrapper.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index 8f58e6691..dbca839e9 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -32,6 +32,7 @@ function WidgetWrapper(props: Props) { const { isWidget = false, active = false, index = 0, moveListItem = null, isPreview = false, isTemplate = false, dashboardId, siteId } = props; const widget: any = useObserver(() => props.widget); const isPredefined = widget.metricType === 'predefined'; + const dashboard = useObserver(() => dashboardStore.selectedDashboard); const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -55,7 +56,7 @@ function WidgetWrapper(props: Props) { }) const onDelete = async () => { - dashboardStore.deleteDashboardWidget(dashboardId!, widget.widgetId); + dashboardStore.deleteDashboardWidget(dashboard?.dashboardId, widget.widgetId); // if (await confirm({ // header: 'Confirm', // confirmButton: 'Yes, delete', From f8d88d344cc1b637c6692056def09ed9e524c994 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Apr 2022 20:35:29 +0200 Subject: [PATCH 213/294] fix(ui) - clickmaps breaking when there no events loaded --- .../PageInsightsPanel/PageInsightsPanel.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/frontend/app/components/Session_/PageInsightsPanel/PageInsightsPanel.tsx b/frontend/app/components/Session_/PageInsightsPanel/PageInsightsPanel.tsx index b83018c99..25170792d 100644 --- a/frontend/app/components/Session_/PageInsightsPanel/PageInsightsPanel.tsx +++ b/frontend/app/components/Session_/PageInsightsPanel/PageInsightsPanel.tsx @@ -21,7 +21,8 @@ interface Props { function PageInsightsPanel({ filters, fetchInsights, events = [], insights, urlOptions, host, loading = true }: Props) { - const [insightsFilters, setInsightsFilters] = useState(filters) + const [insightsFilters, setInsightsFilters] = useState(filters) + const defaultValue = (urlOptions && urlOptions[0]) ? urlOptions[0].value : '' const onDateChange = (e) => { const { startDate, endDate, rangeValue } = e; @@ -36,9 +37,11 @@ function PageInsightsPanel({ }, [insights]) useEffect(() => { - const url = insightsFilters.url ? insightsFilters.url : host + urlOptions[0].value; - Player.pause(); - fetchInsights({ ...insightsFilters, url }) + if (urlOptions && urlOptions[0]) { + const url = insightsFilters.url ? insightsFilters.url : host + urlOptions[0].value; + Player.pause(); + fetchInsights({ ...insightsFilters, url }) + } }, [insightsFilters]) const onPageSelect = (e, { name, value }) => { @@ -68,7 +71,7 @@ function PageInsightsPanel({ selection options={ urlOptions } name="url" - defaultValue={urlOptions[0].value} + defaultValue={defaultValue} onChange={ onPageSelect } id="change-dropdown" className="customDropdown" From 970cfa352e5f3caaa2e5d896e308bbfc806663f9 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 15 Apr 2022 21:06:20 +0200 Subject: [PATCH 214/294] fix(backend/pkg/postgres): SQL syntax fix --- backend/pkg/db/postgres/messages-web.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/pkg/db/postgres/messages-web.go b/backend/pkg/db/postgres/messages-web.go index eb4bca364..abde2a4e0 100644 --- a/backend/pkg/db/postgres/messages-web.go +++ b/backend/pkg/db/postgres/messages-web.go @@ -79,7 +79,7 @@ func (conn *Conn) InsertWebPageEvent(sessionID uint64, e *PageEvent) error { NULLIF($9, 0), NULLIF($10, 0), NULLIF($11, 0), NULLIF($12, 0), NULLIF($13, 0), NULLIF($14, 0), NULLIF($15, 0), NULLIF($16, 0), NULLIF($17, 0), NULLIF($18, 0), - '', + '' ) `, sessionID, e.MessageID, e.Timestamp, @@ -227,7 +227,7 @@ func (conn *Conn) InsertWebFetchEvent(sessionID uint64, savePayload bool, e *Fet session_id, timestamp, seq_index, url, host, path, query, request_body, response_body, status_code, method, - duration, success, + duration, success ) VALUES ( $1, $2, $3, $4, $5, $6, $7 From 4c8ab3d381d2e5bc8ba41730773b0735bf064011 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 15 Apr 2022 21:09:21 +0200 Subject: [PATCH 215/294] feat(tracker-axios):3.5.0:sanitiser --- tracker/tracker-axios/README.md | 60 ++++++++++++++++++++++- tracker/tracker-axios/package.json | 2 +- tracker/tracker-axios/src/index.ts | 76 +++++++++++++++++++++++++----- 3 files changed, 124 insertions(+), 14 deletions(-) diff --git a/tracker/tracker-axios/README.md b/tracker/tracker-axios/README.md index 068fe3190..76187b09e 100644 --- a/tracker/tracker-axios/README.md +++ b/tracker/tracker-axios/README.md @@ -32,14 +32,72 @@ Options: captureWhen: (AxiosRequestConfig) => boolean; // default: () => true sessionTokenHeader: string; // default: undefined ignoreHeaders: Array | boolean, // default [ 'Cookie', 'Set-Cookie', 'Authorization' ] + sanitiser: (RequestResponseData) => RequestResponseData | null, // default: undefined + } ``` + By default plugin connects to the static `axios` instance, but you can specify one with the `instance` option. Set `failuresOnly` option to `true` if you want to record only failed requests, when the axios' promise is rejected. You can also [regulate](https://github.com/axios/axios#request-config) axios failing behaviour with the `validateStatus` option. -`captureWhen` parameter allows you to set a filter on what should be captured. The function will be called with the axios config object and expected to return `true` or `false`. +`captureWhen` parameter allows you to set a filter on request should be captured. The function will be called with the axios config object and expected to return `true` or `false`. In case you use [OpenReplay integrations (sentry, bugsnag or others)](https://docs.openreplay.com/integrations), you can use `sessionTokenHeader` option to specify the header name. This header will be appended automatically to the each axios request and will contain OpenReplay session identificator value. You can define list of headers that you don't want to capture with the `ignoreHeaders` options. Set its value to `false` if you want to catch them all (`true` if opposite). By default plugin ignores the list of headers that might be sensetive such as `[ 'Cookie', 'Set-Cookie', 'Authorization' ]`. + +Sanitise sensitive data from fetch request/response or ignore request comletely with `sanitiser`. You can redact fields on the request object by modifying then returning it from the function: + +```typescript +interface RequestData { + body: BodyInit | null | undefined; // whatewer you've put in the init.body in fetch(url, init) + headers: Record; +} + +interface ResponseData { + body: string | Object | null; // Object if response is of JSON type + headers: Record; +} + +interface RequestResponseData { + readonly status: number; + readonly method: string; + url: string; + request: RequestData; + response: ResponseData; +} + +sanitiser: (data: RequestResponseData) => { // sanitise the body or headers + if (data.url === "/auth") { + data.request.body = null + } + + if (data.request.headers['x-auth-token']) { // can also use ignoreHeaders option instead + data.request.headers['x-auth-token'] = 'SANITISED'; + } + + // Sanitise response + if (data.status < 400 && data.response.body.token) { + data.response.body.token = "" + } + + return data +} + +// OR + +sanitiser: data => { // ignore requests that start with /secure + if (data.url.startsWith("/secure")) { + return null + } + return data +} + +// OR + +sanitiser: data => { // sanitise request url: replace all numbers + data.url = data.url.replace(/\d/g, "*") + return data +} +``` diff --git a/tracker/tracker-axios/package.json b/tracker/tracker-axios/package.json index c92d75112..abb545c1d 100644 --- a/tracker/tracker-axios/package.json +++ b/tracker/tracker-axios/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-axios", "description": "Tracker plugin for axios requests recording", - "version": "3.4.3", + "version": "3.5.0", "keywords": [ "axios", "logging", diff --git a/tracker/tracker-axios/src/index.ts b/tracker/tracker-axios/src/index.ts index ff3b9a0bc..e7abce84e 100644 --- a/tracker/tracker-axios/src/index.ts +++ b/tracker/tracker-axios/src/index.ts @@ -4,12 +4,32 @@ import { App, Messages } from '@openreplay/tracker'; import { getExceptionMessage } from '@openreplay/tracker/lib/modules/exception.js'; // TODO: export from tracker root import { buildFullPath } from './url.js'; + +interface RequestData { + body: BodyInit | null | undefined + headers: Record +} + +interface ResponseData { + body: string | Object | null + headers: Record +} + +interface RequestResponseData { + readonly status: number + readonly method: string + url: string + request: RequestData + response: ResponseData +} + export interface Options { sessionTokenHeader?: string; instance: AxiosInstance; failuresOnly: boolean; captureWhen: (AxiosRequestConfig) => boolean; ignoreHeaders: Array | boolean; + sanitiser?: (RequestResponseData) => RequestResponseData | null; } // TODO: test webpack 5 for axios imports @@ -27,6 +47,7 @@ export default function(opts: Partial = {}) { failuresOnly: false, captureWhen: () => true, ignoreHeaders: [ 'Cookie', 'Set-Cookie', 'Authorization' ], + sanitiser: null, }, opts, ); @@ -88,22 +109,53 @@ export default function(opts: Partial = {}) { } } + // TODO: split the code on functions & files // Why can't axios propogate the final request URL somewhere? - const fullURL = buildFullPath(res.config.baseURL, options.instance.getUri(res.config)); + const url = buildFullPath(res.config.baseURL, options.instance.getUri(res.config)) + const method = typeof res.config.method === 'string' ? res.config.method.toUpperCase() : 'GET' + const status = res.status + let reqResData: RequestResponseData | null = { + status, + method, + url, + request: { + headers: reqHs, + body: reqBody, + }, + response: { + headers: resHs, + body: resBody, + }, + } + if (options.sanitiser) { + try { + reqResData.response.body = JSON.parse(resBody) as Object // Why the returning type is "any"? + } catch {} + reqResData = options.sanitiser(reqResData) + if (!reqResData) { + return + } + } + + const getStj = (r: RequestData | ResponseData): string => { + if (r && typeof r.body !== 'string') { + try { + r.body = JSON.stringify(r.body) + } catch { + r.body = "" + //app.log.warn("Openreplay fetch") // TODO: version check + } + } + return JSON.stringify(r) + } app.send( Messages.Fetch( - typeof res.config.method === 'string' ? res.config.method.toUpperCase() : 'GET', - fullURL, - JSON.stringify({ - headers: reqHs, - body: reqBody, - }), - JSON.stringify({ - headers: resHs, - body: resBody, - }), - res.status, + method, + String(reqResData.url), + getStj(reqResData.request), + getStj(reqResData.response), + status, startTime + performance.timing.navigationStart, duration, ), From 6642784f585387c76191289d3549c6e4f64f24e7 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 15 Apr 2022 21:23:15 +0200 Subject: [PATCH 216/294] fix(backend-integrations): no exit on request error --- backend/services/integrations/main.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/services/integrations/main.go b/backend/services/integrations/main.go index e93a7a0cd..e3dd3f05c 100644 --- a/backend/services/integrations/main.go +++ b/backend/services/integrations/main.go @@ -80,13 +80,10 @@ func main() { } sessionID = sessData.ID } - // TODO: send to ready-events topic. Otherwise it have to go through the events worker. + // TODO: send to ready-events topic. Otherwise it have to go through the events worker. producer.Produce(TOPIC_RAW_WEB, sessionID, messages.Encode(event.RawErrorEvent)) case err := <-manager.Errors: log.Printf("Integration error: %v\n", err) - listener.Close() - pg.Close() - os.Exit(0) case i := <-manager.RequestDataUpdates: // log.Printf("Last request integration update: %v || %v\n", i, string(i.RequestData)) if err := pg.UpdateIntegrationRequestData(&i); err != nil { From 09069329ca8e8c5943bb604026e74fbdc42d1725 Mon Sep 17 00:00:00 2001 From: Mehdi Osman Date: Fri, 15 Apr 2022 22:46:50 +0200 Subject: [PATCH 217/294] Update tracker version --- frontend/env.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/env.js b/frontend/env.js index df0044b23..7060fe937 100644 --- a/frontend/env.js +++ b/frontend/env.js @@ -21,7 +21,7 @@ const oss = { MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, ICE_SERVERS: process.env.ICE_SERVERS, - TRACKER_VERSION: '3.5.4' // trackerInfo.version, + TRACKER_VERSION: '3.5.6' // trackerInfo.version, } module.exports = { From 498e562d1b20fe4997bfd26458e562faaa2bc11a Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Sun, 17 Apr 2022 18:22:10 +0200 Subject: [PATCH 218/294] fix(tracker):3.5.7:fetch keepalive size limit check --- tracker/tracker/package.json | 2 +- tracker/tracker/src/webworker/QueueSender.ts | 29 ++++++++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 089f95f24..e5fdefddf 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "3.5.6", + "version": "3.5.7", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/webworker/QueueSender.ts b/tracker/tracker/src/webworker/QueueSender.ts index b2cb4d3eb..d4686539d 100644 --- a/tracker/tracker/src/webworker/QueueSender.ts +++ b/tracker/tracker/src/webworker/QueueSender.ts @@ -1,5 +1,26 @@ const INGEST_PATH = "/v1/web/i" +const KEEPALIVE_SIZE_LIMIT = 64 << 10 // 64 kB + +// function sendXHR(url: string, token: string, batch: Uint8Array): Promise { +// const req = new XMLHttpRequest() +// req.open("POST", url) +// req.setRequestHeader("Authorization", "Bearer " + token) +// return new Promise((res, rej) => { +// req.onreadystatechange = function() { +// if (this.readyState === 4) { +// if (this.status == 0) { +// return; // happens simultaneously with onerror +// } +// res(this) +// } +// } +// req.onerror = rej +// req.send(batch.buffer) +// }) +// } + + export default class QueueSender { private attemptsCount = 0 private busy = false @@ -24,7 +45,6 @@ export default class QueueSender { if (this.busy || !this.token) { this.queue.push(batch) } else { - this.busy = true this.sendBatch(batch) } } @@ -40,6 +60,8 @@ export default class QueueSender { // would be nice to use Beacon API, but it is not available in WebWorker private sendBatch(batch: Uint8Array):void { + this.busy = true + fetch(this.ingestURL, { body: batch, method: 'POST', @@ -47,7 +69,7 @@ export default class QueueSender { "Authorization": "Bearer " + this.token, //"Content-Type": "", }, - keepalive: true, + keepalive: batch.length < KEEPALIVE_SIZE_LIMIT, }) .then(r => { if (r.status === 401) { // TODO: continuous session ? @@ -69,8 +91,9 @@ export default class QueueSender { } }) .catch(e => { + console.warn("OpenReplay:", e) this.retry(batch) - }) // Does it handle offline exceptions (?) + }) } From 333416d201ddac9be2c616ddcd4ee5e20e72808a Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 18 Apr 2022 23:23:11 +0200 Subject: [PATCH 219/294] feat(api): sort session's events by timestamp and messageId --- api/chalicelib/core/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/chalicelib/core/events.py b/api/chalicelib/core/events.py index 3fbb4f70b..3e39ba4bd 100644 --- a/api/chalicelib/core/events.py +++ b/api/chalicelib/core/events.py @@ -88,7 +88,7 @@ def get_by_sessionId2_pg(session_id, project_id, group_clickrage=False): ORDER BY l.timestamp;""", {"project_id": project_id, "session_id": session_id})) rows += cur.fetchall() rows = helper.list_to_camel_case(rows) - rows = sorted(rows, key=lambda k: k["messageId"]) + rows = sorted(rows, key=lambda k: (k["timestamp"], k["messageId"])) return rows From 0e3dc4352fe227b68a593178387a7de256904ace Mon Sep 17 00:00:00 2001 From: Mehdi Osman Date: Tue, 19 Apr 2022 11:40:26 +0200 Subject: [PATCH 220/294] Update tracker version --- frontend/env.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/env.js b/frontend/env.js index 7060fe937..dc9da4d8b 100644 --- a/frontend/env.js +++ b/frontend/env.js @@ -21,7 +21,7 @@ const oss = { MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, ICE_SERVERS: process.env.ICE_SERVERS, - TRACKER_VERSION: '3.5.6' // trackerInfo.version, + TRACKER_VERSION: '3.5.5' // trackerInfo.version, } module.exports = { From 55f0ff1ad585a9d54d6e1d1045f238c023f16528 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 19 Apr 2022 11:21:19 +0200 Subject: [PATCH 221/294] change(ui) - timeline jump with delay --- .../components/Session_/Player/Controls/DraggableCircle.tsx | 2 +- .../app/components/Session_/Player/Controls/Timeline.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/Session_/Player/Controls/DraggableCircle.tsx b/frontend/app/components/Session_/Player/Controls/DraggableCircle.tsx index 9bdf37651..f4ebd6abf 100644 --- a/frontend/app/components/Session_/Player/Controls/DraggableCircle.tsx +++ b/frontend/app/components/Session_/Player/Controls/DraggableCircle.tsx @@ -19,7 +19,7 @@ function getStyles( // because IE will ignore our custom "empty image" drag preview. opacity: isDragging ? 0 : 1, height: isDragging ? 0 : '', - zIndex: '99999', + zIndex: '99', cursor: 'move' } } diff --git a/frontend/app/components/Session_/Player/Controls/Timeline.js b/frontend/app/components/Session_/Player/Controls/Timeline.js index 69b228d7d..51c4144b5 100644 --- a/frontend/app/components/Session_/Player/Controls/Timeline.js +++ b/frontend/app/components/Session_/Player/Controls/Timeline.js @@ -10,6 +10,7 @@ import { TYPES } from 'Types/session/event'; import { setTimelinePointer } from 'Duck/sessions'; import DraggableCircle from './DraggableCircle'; import CustomDragLayer from './CustomDragLayer'; +import { debounce } from 'App/utils'; const getPointerIcon = (type) => { // exception, @@ -52,6 +53,8 @@ const getPointerIcon = (type) => { return 'info'; } + +let deboucneJump = () => null; @connectPlayer(state => ({ playing: state.playing, time: state.time, @@ -95,6 +98,7 @@ export default class Timeline extends React.PureComponent { componentDidMount() { const { issues, skipToIssue } = this.props; const firstIssue = issues.get(0); + deboucneJump = debounce(this.props.jump, 500); if (firstIssue && skipToIssue) { this.props.jump(firstIssue.time); @@ -112,7 +116,7 @@ export default class Timeline extends React.PureComponent { const p = (offset.x - 60) / this.progressRef.current.offsetWidth; const time = Math.max(Math.round(p * endTime), 0); - this.props.jump(time); + deboucneJump(time); if (this.props.playing) { this.wasPlaying = true; this.props.pause(); From 7f7a7e70cc69d507aebb32a3c4527d73a1f7b5db Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 19 Apr 2022 12:18:27 +0200 Subject: [PATCH 222/294] fix(ui) - dashbaord fixes --- .../Dashboard/components/DashboardModal/DashboardModal.tsx | 4 +++- .../Dashboard/components/WidgetChart/WidgetChart.tsx | 1 - .../Dashboard/components/WidgetWrapper/WidgetWrapper.tsx | 2 +- frontend/app/mstore/dashboardStore.ts | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx index 15b26c809..575cb7de8 100644 --- a/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx +++ b/frontend/app/components/Dashboard/components/DashboardModal/DashboardModal.tsx @@ -23,7 +23,9 @@ function DashboardModal(props) { const onSave = () => { dashboardStore.save(dashboard).then(hideModal).then(() => { - dashboardStore.fetch(dashboard.dashboardId) + if (dashboard.exists()) { + dashboardStore.fetch(dashboard.dashboardId) + } }) } diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index 0f073e8e3..55552a78f 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -13,7 +13,6 @@ import { getStartAndEndTimestampsByDensity } from 'Types/dashboard/helper'; interface Props { metric: any; isWidget?: boolean - onClick?: () => void; } function WidgetChart(props: Props) { const { isWidget = false, metric } = props; diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx index dbca839e9..75b074281 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetWrapper.tsx @@ -69,7 +69,7 @@ function WidgetWrapper(props: Props) { const onChartClick = () => { if (!isWidget || isPredefined) return; - props.history.push(withSiteId(dashboardMetricDetails(dashboardId, widget.metricId),siteId)); + props.history.push(withSiteId(dashboardMetricDetails(dashboard?.dashboardId, widget.metricId),siteId)); } const ref: any = useRef(null) diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 503ea285f..65de46ce4 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -222,6 +222,7 @@ export default class DashboardStore implements IDashboardSotre { if (isCreating) { toast.success('Dashboard created successfully') this.addDashboard(_dashboard) + console.log('_dashboard', _dashboard) } else { toast.success('Dashboard updated successfully') this.updateDashboard(_dashboard) @@ -295,7 +296,7 @@ export default class DashboardStore implements IDashboardSotre { } addDashboard(dashboard: Dashboard) { - this.dashboards.push(dashboard) + this.dashboards.push(new Dashboard().fromJson(dashboard)) } removeDashboard(dashboard: Dashboard) { From 45a1e625ffff31d9438db34d5858bb67a1e59cdc Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 19 Apr 2022 14:44:18 +0200 Subject: [PATCH 223/294] fix(ui) - dashbaord fixes --- .../SpeedIndexByLocation.css | 38 +- .../SpeedIndexByLocation.tsx | 119 ++- frontend/app/mstore/dashboardStore.ts | 2 +- frontend/app/styles/main.css | 12 +- frontend/app/utils.js | 9 +- frontend/package-lock.json | 989 +----------------- frontend/package.json | 1 - 7 files changed, 160 insertions(+), 1010 deletions(-) diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.css b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.css index ab474e37b..a42f4af12 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.css +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.css @@ -6,16 +6,50 @@ stroke-linecap: round; stroke-linejoin: round; margin-top: -20px; - } .location { fill: $gray-light !important; cursor: pointer; + stroke: #fff; &:focus, &:hover { - fill: #b8e2b3; + fill: $teal !important; outline: 0; } +} + +.heat_index0 { + fill:$gray-light !important; +} + +.heat_index5 { + fill: #3EAAAF !important; +} + +.heat_index4 { + fill:#5FBABF !important; +} + +.heat_index3 { + fill: #7BCBCF !important; +} + +.heat_index2 { + fill: #96DCDF !important; +} + +.heat_index1 { + fill: #ADDCDF !important; +} + +.tooltip { + position: fixed; + padding: 5px; + border: 1px solid $gray-light; + border-radius: 3px; + background-color: white; + font-size: 12px; + line-height: 1.2; } \ No newline at end of file diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.tsx index 788e4f020..52f125bcc 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/SpeedIndexByLocation/SpeedIndexByLocation.tsx @@ -3,13 +3,24 @@ import { NoContent } from 'UI'; import { Styles, AvgLabel } from '../../common'; import Scale from './Scale'; import { threeLetter } from 'App/constants/countries'; -import { colorScale } from 'App/utils'; +// import { colorScale } from 'App/utils'; import { observer } from 'mobx-react-lite'; import { numberWithCommas } from 'App/utils'; import WorldMap from "@svg-maps/world"; import { SVGMap } from "react-svg-map"; import "react-svg-map/lib/index.css"; import stl from './SpeedIndexByLocation.css'; +import cn from 'classnames'; + +const getPercentageOfAverage = (data, value: number) => { + const avg = data.reduce((acc, item) => acc + item.avg, 0) / data.length; + return Math.round((value / avg) * 100); +} + +const numberPartBetweenRange = (value: any, max: number) => { + const minPart = value * max / 100 + return Math.round(minPart); +} interface Props { metric?: any @@ -18,56 +29,65 @@ function SpeedIndexByLocation(props: Props) { const { metric } = props; const wrapper: any = React.useRef(null); let map: any = null; - - const getSeries = data => { - const series: any[] = []; - data.forEach(item => { - const d = [threeLetter[item.userCountry], Math.round(item.avg)] - series.push(d) - }) - - return series; - } - - useEffect(() => { - // if (!wrapper && !wrapper.current) return - + const [tooltipStyle, setTooltipStyle] = React.useState({ display: 'none' }); + const [pointedLocation, setPointedLocation] = React.useState(null); + const dataMap = React.useMemo(() => { + const data = {}; + metric.data.chart.forEach((item: any) => { + item.percentage = getPercentageOfAverage(metric.data.chart, item.avg); + item.perNumber = numberPartBetweenRange(item.percentage, 5); + data[item.userCountry.toLowerCase()] = item; + }); + return data; }, []) - // useEffect(() => { - // if (map && map.updateChoropleth) { - // const series = getSeries(metric.data.chart); - // // console.log('series', series) - // // map.updateChoropleth(series, {reset: true}); - // } - // }, []) + // const getSeries = data => { + // const series: any[] = []; + // data.forEach(item => { + // const d = [threeLetter[item.userCountry], Math.round(item.avg)] + // series.push(d) + // }) - const getDataset = () => { - const { metric } = props; - const colors = Styles.colors; - - var dataset = {}; - const series = getSeries(metric.data.chart); - var onlyValues = series.map(function(obj){ return obj[1]; }); - const paletteScale = colorScale(onlyValues, [...colors].reverse()); - - // fill dataset in appropriate format - series.forEach(function(item){ - var iso = item[0], value = item[1]; - dataset[iso] = { numberOfThings: value, fillColor: paletteScale(value) }; - }); - return dataset; - } + // return series; + // } const getLocationClassName = (location, index) => { - // Generate random heat map - return `svg-map__location svg-map__location--heat${index % 4}`; + const i = (dataMap[location.id] ? dataMap[location.id].perNumber : 0); + const cls = stl["heat_index" + i]; + return cn(stl.location, cls); + } + + const getLocationName = (event) => { + if (!event) return null + const id = event.target.attributes.id.value; + const name = event.target.attributes.name.value; + const percentage = dataMap[id] ? dataMap[id].perNumber : 0; + return { name, id, percentage } + } + + const handleLocationMouseOver = (event) => { + const pointedLocation = getLocationName(event); + setPointedLocation(pointedLocation); + } + + const handleLocationMouseOut = () => { + setTooltipStyle({ display: 'none' }); + setPointedLocation(null); + } + + const handleLocationMouseMove = (event) => { + const tooltipStyle = { + display: 'block', + top: event.clientY + 10, + left: event.clientX - 100 + }; + setTooltipStyle(tooltipStyle); } return ( @@ -88,13 +108,20 @@ function SpeedIndexByLocation(props: Props) { console.log(e.target)} + locationClassName={getLocationClassName} + onLocationMouseOver={handleLocationMouseOver} + onLocationMouseOut={handleLocationMouseOut} + onLocationMouseMove={handleLocationMouseMove} />
- {/*
- {this.state.pointedLocation} -
*/} +
+ {pointedLocation && ( + <> +
{pointedLocation.name}
+
Avg: {dataMap[pointedLocation.id] ? numberWithCommas(parseInt(dataMap[pointedLocation.id].avg)) : 0}
+ + )} +
); } diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 65de46ce4..2f7ec0d66 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -360,7 +360,7 @@ export default class DashboardStore implements IDashboardSotre { response.forEach(category => { const widgets: any[] = [] // TODO speed_location is not supported yet - category.widgets.filter(w => w.predefinedKey !== 'speed_location').forEach(widget => { + category.widgets.filter(w => w.predefinedKey !== 'speed_locations').forEach(widget => { const w = new Widget().fromJson(widget) widgets.push(w) }) diff --git a/frontend/app/styles/main.css b/frontend/app/styles/main.css index d59334bd2..4258e43d7 100644 --- a/frontend/app/styles/main.css +++ b/frontend/app/styles/main.css @@ -149,9 +149,11 @@ padding-right: 15px; } -.svg-map { - &__location { - fill: #ddd !important; +/* .svg-map__location { + fill: #EEE !important; cursor: pointer; - } -} \ No newline at end of file + + &:hover { + fill: #fff !important; + } +} */ \ No newline at end of file diff --git a/frontend/app/utils.js b/frontend/app/utils.js index 5ea05633c..8ab0018b2 100644 --- a/frontend/app/utils.js +++ b/frontend/app/utils.js @@ -1,5 +1,5 @@ import JSBI from 'jsbi'; -import { scale } from "d3"; +// import { scale } from "d3"; export function debounce(callback, wait, context = this) { let timeout = null; @@ -197,10 +197,11 @@ export const hashProjectID = (id) => { export const colorScale = (values, colors) => { const minValue = Math.min.apply(null, values); const maxValue = Math.max.apply(null, values); + return [] - return scale.linear() - .domain([minValue,maxValue]) - .range([colors[0], colors[colors.length - 1]]); + // return scale.linear() + // .domain([minValue,maxValue]) + // .range([colors[0], colors[colors.length - 1]]); } export const truncate = (input, max = 10) => input.length > max ? `${input.substring(0, max)}...` : input; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5fa927e01..3696726a6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,7 +13,6 @@ "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", @@ -4584,11 +4583,6 @@ "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", @@ -6438,51 +6432,6 @@ "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", @@ -6650,18 +6599,11 @@ "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==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/buffer-indexof": { "version": "1.1.1", @@ -7520,6 +7462,7 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, "engines": [ "node >= 0.8" ], @@ -7534,6 +7477,7 @@ "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", @@ -7548,6 +7492,7 @@ "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" } @@ -7626,6 +7571,7 @@ "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==", + "dev": true, "dependencies": { "safe-buffer": "~5.1.1" } @@ -8808,11 +8754,6 @@ "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", @@ -8833,14 +8774,6 @@ "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", @@ -8854,11 +8787,6 @@ "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", @@ -8899,16 +8827,6 @@ "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", @@ -9015,11 +8933,6 @@ "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", @@ -9516,36 +9429,6 @@ "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", @@ -9967,56 +9850,6 @@ "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", @@ -10317,6 +10150,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -10364,6 +10198,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -10844,25 +10679,6 @@ "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", @@ -10896,11 +10712,6 @@ "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", @@ -11484,7 +11295,8 @@ "node_modules/foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true }, "node_modules/foreground-child": { "version": "2.0.0", @@ -13932,7 +13744,8 @@ "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", @@ -14324,18 +14137,6 @@ "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", @@ -14506,14 +14307,6 @@ "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", @@ -14744,22 +14537,6 @@ "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", @@ -15961,38 +15738,6 @@ "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", @@ -18132,14 +17877,6 @@ "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", @@ -18200,7 +17937,8 @@ "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==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "node_modules/promise": { "version": "7.3.1", @@ -18528,50 +18266,6 @@ } ] }, - "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", @@ -20283,15 +19977,11 @@ "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==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/safe-regex": { "version": "1.1.0", @@ -20639,44 +20329,11 @@ "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", @@ -21270,52 +20927,6 @@ "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", @@ -21418,63 +21029,6 @@ "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", @@ -22689,27 +22243,6 @@ "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", @@ -22927,17 +22460,6 @@ "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", @@ -23127,7 +22649,8 @@ "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true }, "node_modules/typescript": { "version": "3.9.10", @@ -23772,11 +23295,6 @@ "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", @@ -25860,14 +25378,6 @@ "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", @@ -29357,11 +28867,6 @@ "postcss": "7.x.x" } }, - "@types/d3": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@types/d3/-/d3-3.5.38.tgz", - "integrity": "sha1-dvjy6RWa5WKWWy+g5vvuGqZDobw=" - }, "@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -30918,50 +30423,6 @@ "fill-range": "^7.0.1" } }, - "brfs": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", - "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", - "requires": { - "quote-stream": "^1.0.1", - "resolve": "^1.1.5", - "static-module": "^2.2.0", - "through2": "^2.0.0" - }, - "dependencies": { - "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==", - "requires": { - "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" - } - }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -31098,15 +30559,11 @@ "isarray": "^1.0.0" } }, - "buffer-equal": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", - "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=" - }, "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==" + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "buffer-indexof": { "version": "1.1.1", @@ -31791,6 +31248,7 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, "requires": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", @@ -31802,6 +31260,7 @@ "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, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -31816,6 +31275,7 @@ "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" } @@ -31875,6 +31335,7 @@ "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==", + "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -32845,11 +32306,6 @@ "type": "^1.0.1" } }, - "d3": { - "version": "3.5.17", - "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz", - "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=" - }, "d3-array": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", @@ -32870,14 +32326,6 @@ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" }, - "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=", - "requires": { - "brfs": "^1.3.0" - } - }, "d3-interpolate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", @@ -32891,11 +32339,6 @@ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" }, - "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=" - }, "d3-scale": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", @@ -32936,16 +32379,6 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, - "datamaps": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/datamaps/-/datamaps-0.5.9.tgz", - "integrity": "sha512-GUXpO713URNzaExVUgBtqA5fr2UuxUG/fVitI04zEFHVL2FHSjd672alHq8E16oQqRNzF0m1bmx8WlTnDrGSqQ==", - "requires": { - "@types/d3": "3.5.38", - "d3": "^3.5.6", - "topojson": "^1.6.19" - } - }, "date-fns": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", @@ -33024,11 +32457,6 @@ "regexp.prototype.flags": "^1.2.0" } }, - "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==" - }, "deep-object-diff": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.7.tgz", @@ -33463,38 +32891,6 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "requires": { - "readable-stream": "^2.0.2" - }, - "dependencies": { - "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==", - "requires": { - "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" - } - }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -33853,36 +33249,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, - "escodegen": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", - "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - }, - "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 - } - } - }, "eslint-config-airbnb": { "version": "16.1.0", "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz", @@ -34121,7 +33487,8 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true }, "esrecurse": { "version": "4.3.0", @@ -34152,7 +33519,8 @@ "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true }, "etag": { "version": "1.8.1", @@ -34540,24 +33908,6 @@ "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==", "dev": true }, - "falafel": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.4.tgz", - "integrity": "sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ==", - "requires": { - "acorn": "^7.1.1", - "foreach": "^2.0.5", - "isarray": "^2.0.1", - "object-keys": "^1.0.6" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - } - } - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -34588,11 +33938,6 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, "fast-xml-parser": { "version": "3.21.1", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz", @@ -35072,7 +34417,8 @@ "foreach": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true }, "foreground-child": { "version": "2.0.0", @@ -36940,7 +36286,8 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true }, "isexe": { "version": "2.0.0", @@ -37249,15 +36596,6 @@ } } }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "lilconfig": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", @@ -37396,14 +36734,6 @@ "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" }, - "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==", - "requires": { - "vlq": "^0.2.2" - } - }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -37590,21 +36920,6 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, - "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=", - "requires": { - "source-map": "^0.5.6" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - } - } - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -38573,34 +37888,6 @@ } } }, - "optimist": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", - "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", - "requires": { - "wordwrap": "~0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" - } - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "requires": { - "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" - } - }, "original": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", @@ -40309,11 +39596,6 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" - }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", @@ -40356,7 +39638,8 @@ "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==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "promise": { "version": "7.3.1", @@ -40613,49 +39896,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "quote-stream": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", - "integrity": "sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=", - "requires": { - "buffer-equal": "0.0.1", - "minimist": "^1.1.3", - "through2": "^2.0.0" - }, - "dependencies": { - "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==", - "requires": { - "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" - } - }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, "raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -42021,15 +41261,11 @@ "aproba": "^1.1.1" } }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, "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==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -42342,38 +41578,11 @@ "kind-of": "^6.0.2" } }, - "shallow-copy": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", - "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=" - }, "shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, - "shapefile": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/shapefile/-/shapefile-0.3.1.tgz", - "integrity": "sha1-m7mkKb1ghqDPsDli0Uz99CD/uhI=", - "requires": { - "d3-queue": "1", - "iconv-lite": "0.2", - "optimist": "0.3" - }, - "dependencies": { - "d3-queue": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-1.2.3.tgz", - "integrity": "sha1-FDpwHPpl/gISkvMhwQ0U6Yq9SRs=" - }, - "iconv-lite": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", - "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" - } - } - }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -42870,39 +42079,6 @@ "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==", "dev": true }, - "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==", - "requires": { - "escodegen": "^1.11.1" - }, - "dependencies": { - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - }, - "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 - } - } - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -42987,65 +42163,6 @@ } } }, - "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==", - "requires": { - "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" - }, - "dependencies": { - "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==" - }, - "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==", - "requires": { - "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" - } - }, - "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==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - } - } - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -44003,19 +43120,6 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, - "topojson": { - "version": "1.6.27", - "resolved": "https://registry.npmjs.org/topojson/-/topojson-1.6.27.tgz", - "integrity": "sha1-rb4zpn4vFnPTON8SZErSD8ILQu0=", - "requires": { - "d3": "3", - "d3-geo-projection": "0.2", - "d3-queue": "2", - "optimist": "0.3", - "rw": "1", - "shapefile": "0.3" - } - }, "toposort": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", @@ -44181,14 +43285,6 @@ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "requires": { - "prelude-ls": "~1.1.2" - } - }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -44332,7 +43428,8 @@ "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true }, "typescript": { "version": "3.9.10", @@ -44809,11 +43906,6 @@ "unist-util-stringify-position": "^2.0.0" } }, - "vlq": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", - "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==" - }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", @@ -46513,11 +45605,6 @@ } } }, - "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==" - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index a613d5919..a50cb702e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,7 +20,6 @@ "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", From d2c97a75c8fb3fa399a530b82d1b4b21774f743d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 19 Apr 2022 17:05:08 +0200 Subject: [PATCH 224/294] feat(assist): utilities EE-WS fixed redis routes --- ee/utilities/server.js | 5 +++++ ee/utilities/servers/websocket-cluster.js | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ee/utilities/server.js b/ee/utilities/server.js index 2c70067f1..df1a1a22f 100644 --- a/ee/utilities/server.js +++ b/ee/utilities/server.js @@ -19,6 +19,11 @@ if (process.env.uws !== "true") { let wsapp = express(); wsapp.use(request_logger("[wsapp]")); wsapp.use(request_logger("[app]")); + wsapp.get([PREFIX, `${PREFIX}/`], (req, res) => { + res.statusCode = 200; + res.end("ok!"); + } + ); wsapp.use(`/heapdump/${process.env.S3_KEY}`, dumps.router); wsapp.use(`${PREFIX}/${process.env.S3_KEY}`, socket.wsRouter); wsapp.enable('trust proxy'); diff --git a/ee/utilities/servers/websocket-cluster.js b/ee/utilities/servers/websocket-cluster.js index 59ab97927..840db4439 100644 --- a/ee/utilities/servers/websocket-cluster.js +++ b/ee/utilities/servers/websocket-cluster.js @@ -125,7 +125,7 @@ const socketsList = async function (req, res) { } respond(res, liveSessions); } -wsRouter.get(`/${process.env.S3_KEY}/sockets-list`, socketsList); +wsRouter.get(`/sockets-list`, socketsList); const socketsListByProject = async function (req, res) { debug && console.log("[WS]looking for available sessions"); @@ -151,7 +151,7 @@ const socketsListByProject = async function (req, res) { } respond(res, liveSessions[_projectKey] || []); } -wsRouter.get(`/${process.env.S3_KEY}/sockets-list/:projectKey`, socketsListByProject); +wsRouter.get(`/sockets-list/:projectKey`, socketsListByProject); const socketsLive = async function (req, res) { debug && console.log("[WS]looking for all available LIVE sessions"); @@ -179,7 +179,7 @@ const socketsLive = async function (req, res) { } respond(res, liveSessions); } -wsRouter.get(`/${process.env.S3_KEY}/sockets-live`, socketsLive); +wsRouter.get(`/sockets-live`, socketsLive); const socketsLiveByProject = async function (req, res) { debug && console.log("[WS]looking for available LIVE sessions"); @@ -208,7 +208,7 @@ const socketsLiveByProject = async function (req, res) { } respond(res, liveSessions[_projectKey] || []); } -wsRouter.get(`/${process.env.S3_KEY}/sockets-live/:projectKey`, socketsLiveByProject); +wsRouter.get(`/sockets-live/:projectKey`, socketsLiveByProject); const findSessionSocketId = async (io, peerId) => { const connected_sockets = await io.in(peerId).fetchSockets(); From 947b31e324030734b2b6ae8a39f816dae21f13d5 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 19 Apr 2022 18:02:20 +0200 Subject: [PATCH 225/294] fix(ui) - review fixes --- .../DomBuildingTime/DomBuildingTime.tsx | 2 +- .../SpeedIndexByLocation.tsx | 34 ++++--------------- frontend/app/utils.js | 15 +++++--- frontend/package-lock.json | 11 ++++++ frontend/package.json | 1 + 5 files changed, 30 insertions(+), 33 deletions(-) diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx index 11c71c34d..60402a309 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/DomBuildingTime/DomBuildingTime.tsx @@ -46,7 +46,7 @@ function DomBuildingTime(props: Props) { /> */}
- + { - const avg = data.reduce((acc, item) => acc + item.avg, 0) / data.length; - return Math.round((value / avg) * 100); -} - -const numberPartBetweenRange = (value: any, max: number) => { - const minPart = value * max / 100 - return Math.round(minPart); -} - interface Props { metric?: any } @@ -33,24 +20,15 @@ function SpeedIndexByLocation(props: Props) { const [pointedLocation, setPointedLocation] = React.useState(null); const dataMap = React.useMemo(() => { const data = {}; + const max = metric.data.chart.reduce((acc, item) => Math.max(acc, item.avg), 0); + const min = metric.data.chart.reduce((acc, item) => Math.min(acc, item.avg), 0); metric.data.chart.forEach((item: any) => { - item.percentage = getPercentageOfAverage(metric.data.chart, item.avg); - item.perNumber = numberPartBetweenRange(item.percentage, 5); + item.perNumber = positionOfTheNumber(min, max, item.avg, 5); data[item.userCountry.toLowerCase()] = item; }); return data; }, []) - // const getSeries = data => { - // const series: any[] = []; - // data.forEach(item => { - // const d = [threeLetter[item.userCountry], Math.round(item.avg)] - // series.push(d) - // }) - - // return series; - // } - const getLocationClassName = (location, index) => { const i = (dataMap[location.id] ? dataMap[location.id].perNumber : 0); const cls = stl["heat_index" + i]; @@ -98,7 +76,7 @@ function SpeedIndexByLocation(props: Props) {
{ export const colorScale = (values, colors) => { - const minValue = Math.min.apply(null, values); - const maxValue = Math.max.apply(null, values); - return [] + // const minValue = Math.min.apply(null, values); + // const maxValue = Math.max.apply(null, values); + // return [] + return chroma.scale(colors) // return scale.linear() // .domain([minValue,maxValue]) @@ -239,4 +240,10 @@ export const sliceListPerPage = (list, page, perPage = 10) => { const start = page * perPage; const end = start + perPage; return list.slice(start, end); +} + +export const positionOfTheNumber = (min, max, value, length) => { + const interval = (max - min) / length; + const position = Math.round((value - min) / interval); + return position; } \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3696726a6..5b5c1de48 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@sentry/browser": "^5.21.1", "@svg-maps/world": "^1.0.1", + "chroma-js": "^2.4.2", "classnames": "^2.2.6", "codemirror": "^5.62.3", "copy-to-clipboard": "^3.3.1", @@ -6974,6 +6975,11 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "node_modules/chroma-js": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz", + "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==" + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -30844,6 +30850,11 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "chroma-js": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz", + "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==" + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index a50cb702e..652d2dee9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,6 +17,7 @@ "dependencies": { "@sentry/browser": "^5.21.1", "@svg-maps/world": "^1.0.1", + "chroma-js": "^2.4.2", "classnames": "^2.2.6", "codemirror": "^5.62.3", "copy-to-clipboard": "^3.3.1", From 03fc6e8ec1c62e037e159a38e72963344bb6a5b6 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 19 Apr 2022 18:24:41 +0200 Subject: [PATCH 226/294] feat(assist): dependencies upgrade --- ee/utilities/package-lock.json | 184 ++++++++++++++++----------------- utilities/package-lock.json | 162 ++++++++++++++--------------- 2 files changed, 172 insertions(+), 174 deletions(-) diff --git a/ee/utilities/package-lock.json b/ee/utilities/package-lock.json index 66fdb346f..98ef3f745 100644 --- a/ee/utilities/package-lock.json +++ b/ee/utilities/package-lock.json @@ -1,13 +1,13 @@ { - "name": "utilities_server", + "name": "utilities-server", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "utilities_server", + "name": "utilities-server", "version": "1.0.0", - "license": "MIT", + "license": "Elastic License 2.0 (ELv2)", "dependencies": { "@maxmind/geoip2-node": "^3.4.0", "@socket.io/redis-adapter": "^7.1.0", @@ -38,9 +38,9 @@ } }, "node_modules/@node-redis/client": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.4.tgz", - "integrity": "sha512-IM/NRAqg7MvNC3bIRQipXGrEarunrdgvrbAzsd3ty93LSHi/M+ybQulOERQi8a3M+P5BL8HenwXjiIoKm6ml2g==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.5.tgz", + "integrity": "sha512-ESZ3bd1f+od62h4MaBLKum+klVJfA4wAeLHcVQBkoXa1l0viFesOWnakLQqKg+UyrlJhZmXJWtu0Y9v7iTMrig==", "dependencies": { "cluster-key-slot": "1.1.0", "generic-pool": "3.8.2", @@ -68,9 +68,9 @@ } }, "node_modules/@node-redis/search": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.3.tgz", - "integrity": "sha512-rsrzkGWI84di/uYtEctS/4qLusWt0DESx/psjfB0TFpORDhe7JfC0h8ary+eHulTksumor244bXLRSqQXbFJmw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz", + "integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==", "peerDependencies": { "@node-redis/client": "^1.0.0" } @@ -121,9 +121,9 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" }, "node_modules/@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==" + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", + "integrity": "sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==" }, "node_modules/accepts": { "version": "1.3.8", @@ -290,9 +290,9 @@ } }, "node_modules/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==", "dependencies": { "ms": "2.1.2" }, @@ -362,26 +362,6 @@ "node": ">=10.0.0" } }, - "node_modules/engine.io/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/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -594,12 +574,12 @@ } }, "node_modules/maxmind": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.5.tgz", - "integrity": "sha512-ak0TABuO664C5zXyQH5u13WmtdTwxxXLGOy1e51ZRrp/cEH9xfOcG20F51TcNhVyDos13Ys94kxN8/elg9Ri4Q==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.6.tgz", + "integrity": "sha512-CwnEZqJX0T6b2rWrc0/V3n9hL/hWAMEn7fY09077YJUHiHx7cn/esA2ZIz8BpYLSJUf7cGVel0oUJa9jMwyQpg==", "dependencies": { "mmdb-lib": "2.0.2", - "tiny-lru": "7.0.6" + "tiny-lru": "8.0.2" }, "engines": { "node": ">=10", @@ -639,19 +619,19 @@ } }, "node_modules/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==", "engines": { "node": ">= 0.6" } }, "node_modules/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==", "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -773,15 +753,15 @@ } }, "node_modules/redis": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.4.tgz", - "integrity": "sha512-KaM1OAj/nGrSeybmmOWSMY0LXTGT6FVWgUZZrd2MYzXKJ+VGtqVaciGQeNMfZiQX+kDM8Ke4uttb54m2rm6V0A==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz", + "integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==", "dependencies": { "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.4", + "@node-redis/client": "1.0.5", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.3", + "@node-redis/search": "1.0.5", "@node-redis/time-series": "1.0.2" } }, @@ -931,9 +911,9 @@ } }, "node_modules/tiny-lru": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz", - "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", + "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==", "engines": { "node": ">=6" } @@ -1033,6 +1013,26 @@ "extsprintf": "^1.2.0" } }, + "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/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -1058,9 +1058,9 @@ "requires": {} }, "@node-redis/client": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.4.tgz", - "integrity": "sha512-IM/NRAqg7MvNC3bIRQipXGrEarunrdgvrbAzsd3ty93LSHi/M+ybQulOERQi8a3M+P5BL8HenwXjiIoKm6ml2g==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@node-redis/client/-/client-1.0.5.tgz", + "integrity": "sha512-ESZ3bd1f+od62h4MaBLKum+klVJfA4wAeLHcVQBkoXa1l0viFesOWnakLQqKg+UyrlJhZmXJWtu0Y9v7iTMrig==", "requires": { "cluster-key-slot": "1.1.0", "generic-pool": "3.8.2", @@ -1081,9 +1081,9 @@ "requires": {} }, "@node-redis/search": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.3.tgz", - "integrity": "sha512-rsrzkGWI84di/uYtEctS/4qLusWt0DESx/psjfB0TFpORDhe7JfC0h8ary+eHulTksumor244bXLRSqQXbFJmw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz", + "integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==", "requires": {} }, "@node-redis/time-series": { @@ -1124,9 +1124,9 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" }, "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==" + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", + "integrity": "sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==" }, "accepts": { "version": "1.3.8", @@ -1253,9 +1253,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" } @@ -1295,14 +1295,6 @@ "debug": "~4.3.1", "engine.io-parser": "~5.0.3", "ws": "~8.2.3" - }, - "dependencies": { - "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} - } } }, "engine.io-parser": { @@ -1490,12 +1482,12 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==" }, "maxmind": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.5.tgz", - "integrity": "sha512-ak0TABuO664C5zXyQH5u13WmtdTwxxXLGOy1e51ZRrp/cEH9xfOcG20F51TcNhVyDos13Ys94kxN8/elg9Ri4Q==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.6.tgz", + "integrity": "sha512-CwnEZqJX0T6b2rWrc0/V3n9hL/hWAMEn7fY09077YJUHiHx7cn/esA2ZIz8BpYLSJUf7cGVel0oUJa9jMwyQpg==", "requires": { "mmdb-lib": "2.0.2", - "tiny-lru": "7.0.6" + "tiny-lru": "8.0.2" } }, "media-typer": { @@ -1519,16 +1511,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "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==" }, "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==", "requires": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" } }, "mmdb-lib": { @@ -1610,15 +1602,15 @@ } }, "redis": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.4.tgz", - "integrity": "sha512-KaM1OAj/nGrSeybmmOWSMY0LXTGT6FVWgUZZrd2MYzXKJ+VGtqVaciGQeNMfZiQX+kDM8Ke4uttb54m2rm6V0A==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.0.6.tgz", + "integrity": "sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==", "requires": { "@node-redis/bloom": "1.0.1", - "@node-redis/client": "1.0.4", + "@node-redis/client": "1.0.5", "@node-redis/graph": "1.0.0", "@node-redis/json": "1.0.2", - "@node-redis/search": "1.0.3", + "@node-redis/search": "1.0.5", "@node-redis/time-series": "1.0.2" } }, @@ -1737,9 +1729,9 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, "tiny-lru": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz", - "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==" + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", + "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==" }, "toidentifier": { "version": "1.0.1", @@ -1799,6 +1791,12 @@ "extsprintf": "^1.2.0" } }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/utilities/package-lock.json b/utilities/package-lock.json index df0b8c9ef..d4ef1c007 100644 --- a/utilities/package-lock.json +++ b/utilities/package-lock.json @@ -1,13 +1,13 @@ { - "name": "utilities_server", + "name": "utilities-server", "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "utilities_server", + "name": "utilities-server", "version": "1.0.0", - "license": "MIT", + "license": "Elastic License 2.0 (ELv2)", "dependencies": { "@maxmind/geoip2-node": "^3.4.0", "express": "^4.17.1", @@ -50,9 +50,9 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" }, "node_modules/@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==" + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", + "integrity": "sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==" }, "node_modules/accepts": { "version": "1.3.8", @@ -263,9 +263,9 @@ } }, "node_modules/engine.io/node_modules/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==", "dependencies": { "ms": "2.1.2" }, @@ -283,26 +283,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/engine.io/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/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -481,12 +461,12 @@ } }, "node_modules/maxmind": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.5.tgz", - "integrity": "sha512-ak0TABuO664C5zXyQH5u13WmtdTwxxXLGOy1e51ZRrp/cEH9xfOcG20F51TcNhVyDos13Ys94kxN8/elg9Ri4Q==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.6.tgz", + "integrity": "sha512-CwnEZqJX0T6b2rWrc0/V3n9hL/hWAMEn7fY09077YJUHiHx7cn/esA2ZIz8BpYLSJUf7cGVel0oUJa9jMwyQpg==", "dependencies": { "mmdb-lib": "2.0.2", - "tiny-lru": "7.0.6" + "tiny-lru": "8.0.2" }, "engines": { "node": ">=10", @@ -526,19 +506,19 @@ } }, "node_modules/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==", "engines": { "node": ">= 0.6" } }, "node_modules/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==", "dependencies": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -760,9 +740,9 @@ } }, "node_modules/socket.io-parser/node_modules/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==", "dependencies": { "ms": "2.1.2" }, @@ -781,9 +761,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/socket.io/node_modules/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==", "dependencies": { "ms": "2.1.2" }, @@ -810,9 +790,9 @@ } }, "node_modules/tiny-lru": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz", - "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", + "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==", "engines": { "node": ">=6" } @@ -902,6 +882,26 @@ "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } + }, + "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 + } + } } }, "dependencies": { @@ -937,9 +937,9 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" }, "@types/node": { - "version": "17.0.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", - "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==" + "version": "17.0.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", + "integrity": "sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==" }, "accepts": { "version": "1.3.8", @@ -1091,9 +1091,9 @@ }, "dependencies": { "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" } @@ -1102,12 +1102,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} } } }, @@ -1261,12 +1255,12 @@ "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==" }, "maxmind": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.5.tgz", - "integrity": "sha512-ak0TABuO664C5zXyQH5u13WmtdTwxxXLGOy1e51ZRrp/cEH9xfOcG20F51TcNhVyDos13Ys94kxN8/elg9Ri4Q==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.6.tgz", + "integrity": "sha512-CwnEZqJX0T6b2rWrc0/V3n9hL/hWAMEn7fY09077YJUHiHx7cn/esA2ZIz8BpYLSJUf7cGVel0oUJa9jMwyQpg==", "requires": { "mmdb-lib": "2.0.2", - "tiny-lru": "7.0.6" + "tiny-lru": "8.0.2" } }, "media-typer": { @@ -1290,16 +1284,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "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==" }, "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==", "requires": { - "mime-db": "1.51.0" + "mime-db": "1.52.0" } }, "mmdb-lib": { @@ -1442,9 +1436,9 @@ }, "dependencies": { "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" } @@ -1472,9 +1466,9 @@ }, "dependencies": { "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" } @@ -1492,9 +1486,9 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, "tiny-lru": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-7.0.6.tgz", - "integrity": "sha512-zNYO0Kvgn5rXzWpL0y3RS09sMK67eGaQj9805jlK9G6pSadfriTczzLHFXa/xcW4mIRfmlB9HyQ/+SgL0V1uow==" + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-8.0.2.tgz", + "integrity": "sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==" }, "toidentifier": { "version": "1.0.1", @@ -1544,6 +1538,12 @@ "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } + }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} } } } From d8e832be5661ac87228201e933ba41daad373d17 Mon Sep 17 00:00:00 2001 From: Mehdi Osman Date: Tue, 19 Apr 2022 18:29:06 +0200 Subject: [PATCH 227/294] Update env.js --- frontend/env.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/env.js b/frontend/env.js index dc9da4d8b..45f2787e7 100644 --- a/frontend/env.js +++ b/frontend/env.js @@ -13,7 +13,7 @@ const oss = { ORIGIN: () => 'window.location.origin', API_EDP: () => 'window.location.origin + "/api"', ASSETS_HOST: () => 'window.location.origin + "/assets"', - VERSION: '1.5.5', + VERSION: '1.6.0', SOURCEMAP: true, MINIO_ENDPOINT: process.env.MINIO_ENDPOINT, MINIO_PORT: process.env.MINIO_PORT, From 8a292641ffe39cb76ed5870c546d5d84e58b4131 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 19 Apr 2022 18:40:39 +0200 Subject: [PATCH 228/294] feat(assist): dependencies upgrade feat(api): telemetry upgrade feat(db): version change --- api/chalicelib/core/telemetry.py | 3 ++- ee/api/chalicelib/core/telemetry.py | 4 +++- .../init_dbs/postgresql/{1.5.5/1.5.5.sql => 1.6.0/1.6.0.sql} | 2 +- ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql | 2 +- .../init_dbs/postgresql/{1.5.5/1.5.5.sql => 1.6.0/1.6.0.sql} | 2 +- scripts/helm/db/init_dbs/postgresql/init_schema.sql | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) rename ee/scripts/helm/db/init_dbs/postgresql/{1.5.5/1.5.5.sql => 1.6.0/1.6.0.sql} (99%) rename scripts/helm/db/init_dbs/postgresql/{1.5.5/1.5.5.sql => 1.6.0/1.6.0.sql} (99%) diff --git a/api/chalicelib/core/telemetry.py b/api/chalicelib/core/telemetry.py index 28eb97f73..906e9d25e 100644 --- a/api/chalicelib/core/telemetry.py +++ b/api/chalicelib/core/telemetry.py @@ -27,7 +27,8 @@ def compute(): t_projects=COALESCE((SELECT COUNT(*) FROM public.projects WHERE deleted_at ISNULL), 0), t_sessions=COALESCE((SELECT COUNT(*) FROM public.sessions), 0), t_users=COALESCE((SELECT COUNT(*) FROM public.users WHERE deleted_at ISNULL), 0) - RETURNING *,(SELECT email FROM public.users WHERE role='owner' LIMIT 1);""" + RETURNING t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, + (SELECT openreplay_version()) AS version_number,(SELECT email FROM public.users WHERE role = 'owner' LIMIT 1);""" ) data = cur.fetchone() requests.post('https://api.openreplay.com/os/telemetry', json={"stats": [process_data(data)]}) diff --git a/ee/api/chalicelib/core/telemetry.py b/ee/api/chalicelib/core/telemetry.py index e05df7fdc..cc71f53d5 100644 --- a/ee/api/chalicelib/core/telemetry.py +++ b/ee/api/chalicelib/core/telemetry.py @@ -50,7 +50,9 @@ def compute(): FROM public.tenants ) AS all_tenants WHERE tenants.tenant_id = all_tenants.tenant_id - RETURNING *,(SELECT email FROM users_ee WHERE role = 'owner' AND users_ee.tenant_id = tenants.tenant_id LIMIT 1);""" + RETURNING t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, + (SELECT openreplay_version()) AS version_number, + (SELECT email FROM public.users WHERE role = 'owner' AND users.tenant_id=tenants.tenant_id LIMIT 1);""" ) data = cur.fetchall() requests.post('https://api.openreplay.com/os/telemetry', 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.6.0/1.6.0.sql similarity index 99% rename from ee/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql rename to ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql index 628ddc8b3..27b76cbb8 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.6.0/1.6.0.sql @@ -2,7 +2,7 @@ BEGIN; CREATE OR REPLACE FUNCTION openreplay_version() RETURNS text AS $$ -SELECT 'v1.5.5-ee' +SELECT 'v1.6.0-ee' $$ LANGUAGE sql IMMUTABLE; 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 6b67499bd..2f2f34b8d 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.5-ee' +SELECT 'v1.6.0-ee' $$ LANGUAGE sql IMMUTABLE; diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql similarity index 99% rename from scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql rename to scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql index 3f853a597..b507cd511 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql @@ -2,7 +2,7 @@ BEGIN; CREATE OR REPLACE FUNCTION openreplay_version() RETURNS text AS $$ -SELECT 'v1.5.5' +SELECT 'v1.6.0' $$ LANGUAGE sql IMMUTABLE; diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index cc02388f3..158e5b56a 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.5' +SELECT 'v1.6.0' $$ LANGUAGE sql IMMUTABLE; -- --- accounts.sql --- From a51247f22f48ee6992b2c5fa7333c63ff6ac4f61 Mon Sep 17 00:00:00 2001 From: KRAIEM Taha Yassine Date: Tue, 19 Apr 2022 16:42:04 +0000 Subject: [PATCH 229/294] version change --- scripts/helmcharts/init.sh | 2 +- scripts/helmcharts/openreplay/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/alerts/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/assets/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/assist/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/chalice/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/db/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/ender/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/http/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/integrations/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/peers/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/sink/Chart.yaml | 2 +- scripts/helmcharts/openreplay/charts/storage/Chart.yaml | 2 +- scripts/helmcharts/vars.yaml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/helmcharts/init.sh b/scripts/helmcharts/init.sh index f884c1c29..7c846356f 100644 --- a/scripts/helmcharts/init.sh +++ b/scripts/helmcharts/init.sh @@ -15,7 +15,7 @@ fatal() exit 1 } -version="v1.5.5" +version="v1.6.0" usr=`whoami` # Installing k3s diff --git a/scripts/helmcharts/openreplay/Chart.yaml b/scripts/helmcharts/openreplay/Chart.yaml index 4b8e3e6a7..c1875102e 100644 --- a/scripts/helmcharts/openreplay/Chart.yaml +++ b/scripts/helmcharts/openreplay/Chart.yaml @@ -22,7 +22,7 @@ version: 0.1.0 # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. # Ref: https://github.com/helm/helm/issues/7858#issuecomment-608114589 -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" dependencies: - name: ingress-nginx diff --git a/scripts/helmcharts/openreplay/charts/alerts/Chart.yaml b/scripts/helmcharts/openreplay/charts/alerts/Chart.yaml index 4d99d566b..13c06a6f4 100644 --- a/scripts/helmcharts/openreplay/charts/alerts/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/alerts/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/assets/Chart.yaml b/scripts/helmcharts/openreplay/charts/assets/Chart.yaml index 19034cb87..9285d6c89 100644 --- a/scripts/helmcharts/openreplay/charts/assets/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/assets/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/assist/Chart.yaml b/scripts/helmcharts/openreplay/charts/assist/Chart.yaml index fda8cdfa0..30230fe1d 100644 --- a/scripts/helmcharts/openreplay/charts/assist/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/assist/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/chalice/Chart.yaml b/scripts/helmcharts/openreplay/charts/chalice/Chart.yaml index 7f2ef368d..66e0bcafc 100644 --- a/scripts/helmcharts/openreplay/charts/chalice/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/chalice/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/db/Chart.yaml b/scripts/helmcharts/openreplay/charts/db/Chart.yaml index a938ef054..9b98ad883 100644 --- a/scripts/helmcharts/openreplay/charts/db/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/db/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/ender/Chart.yaml b/scripts/helmcharts/openreplay/charts/ender/Chart.yaml index 561c48726..2215e491f 100644 --- a/scripts/helmcharts/openreplay/charts/ender/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/ender/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/http/Chart.yaml b/scripts/helmcharts/openreplay/charts/http/Chart.yaml index c28fc23b4..1deecd8fa 100644 --- a/scripts/helmcharts/openreplay/charts/http/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/http/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/integrations/Chart.yaml b/scripts/helmcharts/openreplay/charts/integrations/Chart.yaml index c8c405266..b5036ab1b 100644 --- a/scripts/helmcharts/openreplay/charts/integrations/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/integrations/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/peers/Chart.yaml b/scripts/helmcharts/openreplay/charts/peers/Chart.yaml index 0fbf35cf5..d1acbe1ca 100644 --- a/scripts/helmcharts/openreplay/charts/peers/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/peers/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/sink/Chart.yaml b/scripts/helmcharts/openreplay/charts/sink/Chart.yaml index a9d2cd310..6b57a6ad1 100644 --- a/scripts/helmcharts/openreplay/charts/sink/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/sink/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/openreplay/charts/storage/Chart.yaml b/scripts/helmcharts/openreplay/charts/storage/Chart.yaml index 667e6046d..c131978ef 100644 --- a/scripts/helmcharts/openreplay/charts/storage/Chart.yaml +++ b/scripts/helmcharts/openreplay/charts/storage/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -AppVersion: "v1.5.5" +AppVersion: "v1.6.0" diff --git a/scripts/helmcharts/vars.yaml b/scripts/helmcharts/vars.yaml index 49d48525b..e53162ac3 100644 --- a/scripts/helmcharts/vars.yaml +++ b/scripts/helmcharts/vars.yaml @@ -1,4 +1,4 @@ -fromVersion: "v1.5.5" +fromVersion: "v1.6.0" # Databases specific variables postgresql: &postgres # For generating passwords From f5f0208966ed0ab89a6c1635c79c614f3bec3736 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 19 Apr 2022 19:01:00 +0200 Subject: [PATCH 230/294] feat(api): dashboard templates order feat(api): dashboard templates category description --- api/chalicelib/core/dashboards2.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index f46c5a8ca..b3ebf9783 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -7,11 +7,11 @@ from chalicelib.utils import pg_client from chalicelib.utils.TimeUTC import TimeUTC CATEGORY_DESCRIPTION = { - 'overview': 'lorem ipsum', - 'custom': 'lorem cusipsum', - 'errors': 'lorem erripsum', - 'performance': 'lorem perfipsum', - 'resources': 'lorem resipsum' + 'overview': 'High-level metrics and web vitals.', + 'custom': 'Previously created custom metrics by me and my team.', + 'errors': 'Keep a closer eye on errors and track their type, origin and domain.', + 'performance': 'Optimize your app’s performance by tracking slow domains, page response times, memory consumption, CPU usage and more.', + 'resources': 'Find out which resources are missing and those that may be slowing your web app.' } @@ -28,7 +28,8 @@ def get_templates(project_id, user_id): 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}) + ORDER BY ARRAY_POSITION(ARRAY ['custom','overview','errors','performance','resources'], category);""", + {"project_id": project_id, "userId": user_id}) cur.execute(pg_query) rows = cur.fetchall() for r in rows: From 1642c1ffd18ab2cbdb04e3b0119b507857713ce2 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 19 Apr 2022 19:10:56 +0200 Subject: [PATCH 231/294] feat(api): changes --- api/chalicelib/core/{dashboards2.py => dashboards.py} | 2 +- api/chalicelib/core/insights.py | 2 +- api/chalicelib/core/{dashboard.py => metrics.py} | 0 api/routers/subs/dashboard.py | 2 +- api/routers/subs/metrics.py | 2 +- ee/api/.gitignore | 2 +- ee/api/chalicelib/core/errors.py | 2 +- ee/api/chalicelib/core/insights.py | 4 ++-- ee/api/chalicelib/core/{dashboard.py => metrics.py} | 0 9 files changed, 8 insertions(+), 8 deletions(-) rename api/chalicelib/core/{dashboards2.py => dashboards.py} (99%) rename api/chalicelib/core/{dashboard.py => metrics.py} (100%) rename ee/api/chalicelib/core/{dashboard.py => metrics.py} (100%) diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards.py similarity index 99% rename from api/chalicelib/core/dashboards2.py rename to api/chalicelib/core/dashboards.py index b3ebf9783..3af8b01a0 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards.py @@ -1,7 +1,7 @@ import json import schemas -from chalicelib.core import custom_metrics, dashboard +from chalicelib.core import custom_metrics, metrics from chalicelib.utils import helper from chalicelib.utils import pg_client from chalicelib.utils.TimeUTC import TimeUTC diff --git a/api/chalicelib/core/insights.py b/api/chalicelib/core/insights.py index d5caa3a61..e5219344b 100644 --- a/api/chalicelib/core/insights.py +++ b/api/chalicelib/core/insights.py @@ -1,5 +1,5 @@ import schemas -from chalicelib.core.dashboard import __get_constraints, __get_constraint_values +from chalicelib.core.metrics import __get_constraints, __get_constraint_values from chalicelib.utils import helper, dev from chalicelib.utils import pg_client from chalicelib.utils.TimeUTC import TimeUTC diff --git a/api/chalicelib/core/dashboard.py b/api/chalicelib/core/metrics.py similarity index 100% rename from api/chalicelib/core/dashboard.py rename to api/chalicelib/core/metrics.py diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 029127cbd..3d27178cf 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -1,7 +1,7 @@ from fastapi import Body import schemas -from chalicelib.core import dashboard +from chalicelib.core import metrics from chalicelib.core import metadata from chalicelib.utils import helper from routers.base import get_routers diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 0a806b146..a5a1cede5 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -1,7 +1,7 @@ from fastapi import Body, Depends import schemas -from chalicelib.core import dashboards2, custom_metrics +from chalicelib.core import dashboards, custom_metrics from or_dependencies import OR_context from routers.base import get_routers diff --git a/ee/api/.gitignore b/ee/api/.gitignore index f8ff0f789..c5a8d9ce4 100644 --- a/ee/api/.gitignore +++ b/ee/api/.gitignore @@ -260,5 +260,5 @@ Pipfile /build_alerts.sh /routers/subs/metrics.py /routers/subs/v1_api.py -/chalicelib/core/dashboards2.py +/chalicelib/core/dashboards.py entrypoint.sh \ No newline at end of file diff --git a/ee/api/chalicelib/core/errors.py b/ee/api/chalicelib/core/errors.py index 8531d89a3..84beb0c30 100644 --- a/ee/api/chalicelib/core/errors.py +++ b/ee/api/chalicelib/core/errors.py @@ -1,7 +1,7 @@ import json import schemas -from chalicelib.core import dashboard +from chalicelib.core import metrics from chalicelib.core import sourcemaps, sessions from chalicelib.utils import ch_client, metrics_helper from chalicelib.utils import pg_client, helper diff --git a/ee/api/chalicelib/core/insights.py b/ee/api/chalicelib/core/insights.py index 9ac11013d..3dba723e4 100644 --- a/ee/api/chalicelib/core/insights.py +++ b/ee/api/chalicelib/core/insights.py @@ -1,6 +1,6 @@ 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.core.metrics import __get_basic_constraints, __get_meta_constraint +from chalicelib.core.metrics 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 diff --git a/ee/api/chalicelib/core/dashboard.py b/ee/api/chalicelib/core/metrics.py similarity index 100% rename from ee/api/chalicelib/core/dashboard.py rename to ee/api/chalicelib/core/metrics.py From c5b0d0fc829fb23b114ea228f06d530732a81e9a Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 19 Apr 2022 19:04:18 +0200 Subject: [PATCH 232/294] fix(ui) - category scroll and font size --- .../DashboardMetricSelection.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx index a999e0358..400654df2 100644 --- a/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx +++ b/frontend/app/components/Dashboard/components/DashboardMetricSelection/DashboardMetricSelection.tsx @@ -4,7 +4,7 @@ import { useObserver } from 'mobx-react-lite'; import cn from 'classnames'; import { useStore } from 'App/mstore'; -function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, unSelectCategory }) { +function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds }) { const selectedCategoryWidgetsCount = useObserver(() => { return category.widgets.filter(widget => selectedWidgetIds.includes(widget.metricId)).length; }); @@ -14,10 +14,9 @@ function WidgetCategoryItem({ category, isSelected, onClick, selectedWidgetIds, onClick={() => onClick(category)} >
{category.name}
-
{category.description}
+
{category.description}
{selectedCategoryWidgetsCount > 0 && (
- {/* unSelectCategory(category)} /> */} {`Selected ${selectedCategoryWidgetsCount} of ${category.widgets.length}`}
)} @@ -69,7 +68,7 @@ function DashboardMetricSelection(props) {
- Showing past 7 days data for visual clue + Past 7 days data
-
+
{activeCategory && widgetCategories.map((category, index) => )}
From 970e230dbc74aad5079b735923b10ff877e3653d Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 19 Apr 2022 19:14:36 +0200 Subject: [PATCH 233/294] fix(ui) - ui review fixes --- frontend/app/components/Dashboard/NewDashboard.tsx | 5 ++--- .../Dashboard/components/DashboardView/DashboardView.tsx | 3 ++- .../Dashboard/components/MetricsView/MetricsView.tsx | 4 ++-- frontend/app/components/Header/Header.js | 2 +- frontend/app/mstore/dashboardStore.ts | 5 ++--- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/frontend/app/components/Dashboard/NewDashboard.tsx b/frontend/app/components/Dashboard/NewDashboard.tsx index 93f3cda8e..9e4168cfc 100644 --- a/frontend/app/components/Dashboard/NewDashboard.tsx +++ b/frontend/app/components/Dashboard/NewDashboard.tsx @@ -1,6 +1,5 @@ import React, { useEffect } from 'react'; -import withPageTitle from 'HOCs/withPageTitle'; -import { observer, useObserver } from "mobx-react-lite"; +import { useObserver } from "mobx-react-lite"; import { useStore } from 'App/mstore'; import { withRouter } from 'react-router-dom'; import DashboardSideMenu from './components/DashboardSideMenu'; @@ -36,4 +35,4 @@ function NewDashboard(props) { )); } -export default withPageTitle('New Dashboard')(withRouter(NewDashboard)); \ No newline at end of file +export default withRouter(NewDashboard); \ 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 b412dc1f2..16803e34c 100644 --- a/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx +++ b/frontend/app/components/Dashboard/components/DashboardView/DashboardView.tsx @@ -12,6 +12,7 @@ import DashboardModal from '../DashboardModal'; import DashboardEditModal from '../DashboardEditModal'; import DateRange from 'Shared/DateRange'; import AlertFormModal from 'App/components/Alerts/AlertFormModal'; +import withPageTitle from 'HOCs/withPageTitle'; interface Props { siteId: number; @@ -125,4 +126,4 @@ function DashboardView(props: Props) { )); } -export default withRouter(withModal(DashboardView)); \ No newline at end of file +export default withPageTitle('Dashboards - OpenReplay')(withRouter(withModal(DashboardView))); \ 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 af296b14a..9d82d6288 100644 --- a/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx +++ b/frontend/app/components/Dashboard/components/MetricsView/MetricsView.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Button, PageTitle, Icon, Link } from 'UI'; -import { withSiteId, metricCreate } from 'App/routes'; +import withPageTitle from 'HOCs/withPageTitle'; import MetricsList from '../MetricsList'; import MetricsSearch from '../MetricsSearch'; import { useStore } from 'App/mstore'; @@ -34,4 +34,4 @@ function MetricsView(props: Props) { )); } -export default MetricsView; \ No newline at end of file +export default withPageTitle('Metrics - OpenReplay')(MetricsView); \ No newline at end of file diff --git a/frontend/app/components/Header/Header.js b/frontend/app/components/Header/Header.js index f3aa726d1..219463499 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 } > - { 'Dashboard' } + { 'Dashboards' }
diff --git a/frontend/app/mstore/dashboardStore.ts b/frontend/app/mstore/dashboardStore.ts index 2f7ec0d66..56c92e4b9 100644 --- a/frontend/app/mstore/dashboardStore.ts +++ b/frontend/app/mstore/dashboardStore.ts @@ -221,11 +221,10 @@ export default class DashboardStore implements IDashboardSotre { runInAction(() => { if (isCreating) { toast.success('Dashboard created successfully') - this.addDashboard(_dashboard) - console.log('_dashboard', _dashboard) + this.addDashboard(new Dashboard().fromJson(_dashboard)) } else { toast.success('Dashboard updated successfully') - this.updateDashboard(_dashboard) + this.updateDashboard(new Dashboard().fromJson(_dashboard)) } resolve(_dashboard) }) From 18c1d9e132be6a231e012de1a135006379a2f00f Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 19 Apr 2022 19:17:25 +0200 Subject: [PATCH 234/294] fix(ui) - ui review fixes --- frontend/app/components/Header/header.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/app/components/Header/header.css b/frontend/app/components/Header/header.css index 427f95e18..e817d80fb 100644 --- a/frontend/app/components/Header/header.css +++ b/frontend/app/components/Header/header.css @@ -25,9 +25,11 @@ $height: 50px; color: $gray-darkest; text-transform: uppercase; white-space: nowrap; + transition: all .2s ease-in-out; &:hover, &.active { color: $teal; border-bottom: 2px solid $teal; + transition: all .2s ease-in-out; } position: relative; } From 4897f4b5d76a00d0e1784850ac6ace30c9191aeb Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Tue, 19 Apr 2022 19:45:22 +0200 Subject: [PATCH 235/294] fix(ui) - project id redirection on creation --- .../components/Client/Sites/NewSiteForm.js | 20 +++++++++++-------- .../ProjectCodeSnippet/ProjectCodeSnippet.js | 10 ++++++---- .../ProjectFormButton/ProjectFormButton.js | 6 ++++-- .../NoSessionsMessage/NoSessionsMessage.js | 2 +- .../ProjectCodeSnippet/ProjectCodeSnippet.js | 3 ++- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/frontend/app/components/Client/Sites/NewSiteForm.js b/frontend/app/components/Client/Sites/NewSiteForm.js index 407da1021..4195c8c63 100644 --- a/frontend/app/components/Client/Sites/NewSiteForm.js +++ b/frontend/app/components/Client/Sites/NewSiteForm.js @@ -17,7 +17,7 @@ import styles from './siteForm.css'; update, pushNewSite, fetchList, - setSiteId + setSiteId }) @withRouter export default class NewSiteForm extends React.PureComponent { @@ -38,13 +38,17 @@ export default class NewSiteForm extends React.PureComponent { }) } else { this.props.save(this.props.site).then(() => { - const { sites } = this.props; - const site = sites.last(); - this.props.pushNewSite(site) - if (!pathname.includes('/client')) { - this.props.setSiteId(site.id) - } - this.props.onClose(null, site) + this.props.fetchList().then(() => { + const { sites } = this.props; + const site = sites.last(); + if (!pathname.includes('/client')) { + console.log('site', site) + this.props.setSiteId(site.get('id')) + } + this.props.onClose(null, site) + }) + + // this.props.pushNewSite(site) }); } } diff --git a/frontend/app/components/Onboarding/components/OnboardingTabs/ProjectCodeSnippet/ProjectCodeSnippet.js b/frontend/app/components/Onboarding/components/OnboardingTabs/ProjectCodeSnippet/ProjectCodeSnippet.js index 7002ed604..fd3f9a7e8 100644 --- a/frontend/app/components/Onboarding/components/OnboardingTabs/ProjectCodeSnippet/ProjectCodeSnippet.js +++ b/frontend/app/components/Onboarding/components/OnboardingTabs/ProjectCodeSnippet/ProjectCodeSnippet.js @@ -19,7 +19,8 @@ const inputModeOptionsMap = {} inputModeOptions.forEach((o, i) => inputModeOptionsMap[o.value] = i) const ProjectCodeSnippet = props => { - const { site, gdpr } = props; + const site = props.sites.find(s => s.id === props.siteId); + const { gdpr } = site; const [changed, setChanged] = useState(false) const [copied, setCopied] = useState(false) @@ -72,7 +73,7 @@ const ProjectCodeSnippet = props => { } const getOptionValues = () => { - const { gdpr } = props.site; + // const { gdpr } = site; return (!!gdpr.maskEmails)|(!!gdpr.maskNumbers << 1)|(['plain' , 'obscured', 'hidden'].indexOf(gdpr.defaultInputMode) << 5)|28 } @@ -164,7 +165,8 @@ const ProjectCodeSnippet = props => { } export default connect(state => ({ - site: state.getIn([ 'site', 'instance' ]), - gdpr: state.getIn([ 'site', 'instance', 'gdpr' ]), + siteId: state.getIn([ 'site', 'siteId' ]), + sites: state.getIn([ 'site', 'list' ]), + // gdpr: state.getIn([ 'site', 'instance', 'gdpr' ]), saving: state.getIn([ 'site', 'saveGDPR', 'loading' ]) }), { editGDPR, saveGDPR })(ProjectCodeSnippet) diff --git a/frontend/app/components/Onboarding/components/ProjectFormButton/ProjectFormButton.js b/frontend/app/components/Onboarding/components/ProjectFormButton/ProjectFormButton.js index c7386fe7b..67dbd6541 100644 --- a/frontend/app/components/Onboarding/components/ProjectFormButton/ProjectFormButton.js +++ b/frontend/app/components/Onboarding/components/ProjectFormButton/ProjectFormButton.js @@ -3,8 +3,9 @@ import { connect } from 'react-redux' import { SlideModal } from 'UI' import NewSiteForm from '../../../Client/Sites/NewSiteForm' -const ProjectFormButton = ({ children, site }) => { +const ProjectFormButton = ({ children, sites, siteId }) => { const [showModal, setShowModal] = useState(false) + const site = sites.find(({ id }) => id === siteId) const closeModal = () => setShowModal(!showModal); @@ -27,5 +28,6 @@ const ProjectFormButton = ({ children, site }) => { } export default connect(state => ({ - site: state.getIn([ 'site', 'instance' ]), + siteId: state.getIn([ 'site', 'siteId' ]), + sites: state.getIn([ 'site', 'list' ]), }))(ProjectFormButton) \ No newline at end of file diff --git a/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js b/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js index cee55088b..bee2810d8 100644 --- a/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js +++ b/frontend/app/components/shared/NoSessionsMessage/NoSessionsMessage.js @@ -35,6 +35,6 @@ const NoSessionsMessage= (props) => { } export default connect(state => ({ - site: state.getIn([ 'site', 'instance' ]), + site: state.getIn([ 'site', 'siteId' ]), sites: state.getIn([ 'site', 'list' ]) }))(withRouter(NoSessionsMessage)) \ No newline at end of file diff --git a/frontend/app/components/shared/TrackingCodeModal/ProjectCodeSnippet/ProjectCodeSnippet.js b/frontend/app/components/shared/TrackingCodeModal/ProjectCodeSnippet/ProjectCodeSnippet.js index 50ca97823..6eadd41d0 100644 --- a/frontend/app/components/shared/TrackingCodeModal/ProjectCodeSnippet/ProjectCodeSnippet.js +++ b/frontend/app/components/shared/TrackingCodeModal/ProjectCodeSnippet/ProjectCodeSnippet.js @@ -19,7 +19,7 @@ inputModeOptions.forEach((o, i) => inputModeOptionsMap[o.value] = i) const ProjectCodeSnippet = props => { - const { site, gdpr, saving } = props; + const { gdpr, site } = props; const [changed, setChanged] = useState(false) const [copied, setCopied] = useState(false) @@ -152,6 +152,7 @@ const ProjectCodeSnippet = props => { } export default connect(state => ({ + // siteId: state.getIn([ 'site', 'siteId' ]), site: state.getIn([ 'site', 'instance' ]), gdpr: state.getIn([ 'site', 'instance', 'gdpr' ]), saving: state.getIn([ 'site', 'saveGDPR', 'loading' ]) From 85446575d52ed42e36c145aff95e0619cb74f7c3 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 19 Apr 2022 19:45:57 +0200 Subject: [PATCH 236/294] feat(api): fixes --- api/chalicelib/core/assist.py | 4 +- api/chalicelib/core/dashboards.py | 90 ++++++++--------- api/routers/subs/dashboard.py | 154 ++++++++++++++--------------- api/routers/subs/metrics.py | 26 ++--- ee/api/app.py | 3 +- ee/api/chalicelib/core/errors.py | 4 +- ee/api/chalicelib/core/projects.py | 11 +++ 7 files changed, 151 insertions(+), 141 deletions(-) diff --git a/api/chalicelib/core/assist.py b/api/chalicelib/core/assist.py index 97bff84fd..1b6e8c405 100644 --- a/api/chalicelib/core/assist.py +++ b/api/chalicelib/core/assist.py @@ -1,9 +1,7 @@ import requests from decouple import config -import schemas -from chalicelib.core import projects, sessions -from chalicelib.utils import pg_client, helper +from chalicelib.core import projects SESSION_PROJECTION_COLS = """s.project_id, s.session_id::text AS session_id, diff --git a/api/chalicelib/core/dashboards.py b/api/chalicelib/core/dashboards.py index 3af8b01a0..7b7bfe252 100644 --- a/api/chalicelib/core/dashboards.py +++ b/api/chalicelib/core/dashboards.py @@ -242,51 +242,51 @@ def create_metric_add_widget(project_id, user_id, dashboard_id, data: schemas.Cr data=schemas.AddWidgetToDashboardPayloadSchema(metricId=metric_id)) -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, - 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, - 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, - 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, +PREDEFINED = {schemas.TemplatePredefinedKeys.count_sessions: metrics.get_processed_sessions, + schemas.TemplatePredefinedKeys.avg_image_load_time: metrics.get_application_activity_avg_image_load_time, + schemas.TemplatePredefinedKeys.avg_page_load_time: metrics.get_application_activity_avg_page_load_time, + schemas.TemplatePredefinedKeys.avg_request_load_time: metrics.get_application_activity_avg_request_load_time, + schemas.TemplatePredefinedKeys.avg_dom_content_load_start: metrics.get_page_metrics_avg_dom_content_load_start, + schemas.TemplatePredefinedKeys.avg_first_contentful_pixel: metrics.get_page_metrics_avg_first_contentful_pixel, + schemas.TemplatePredefinedKeys.avg_visited_pages: metrics.get_user_activity_avg_visited_pages, + schemas.TemplatePredefinedKeys.avg_session_duration: metrics.get_user_activity_avg_session_duration, + schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime: metrics.get_pages_dom_build_time, + schemas.TemplatePredefinedKeys.avg_pages_response_time: metrics.get_pages_response_time, + schemas.TemplatePredefinedKeys.avg_response_time: metrics.get_top_metrics_avg_response_time, + schemas.TemplatePredefinedKeys.avg_first_paint: metrics.get_top_metrics_avg_first_paint, + schemas.TemplatePredefinedKeys.avg_dom_content_loaded: metrics.get_top_metrics_avg_dom_content_loaded, + schemas.TemplatePredefinedKeys.avg_till_first_bit: metrics.get_top_metrics_avg_till_first_bit, + schemas.TemplatePredefinedKeys.avg_time_to_interactive: metrics.get_top_metrics_avg_time_to_interactive, + schemas.TemplatePredefinedKeys.count_requests: metrics.get_top_metrics_count_requests, + schemas.TemplatePredefinedKeys.avg_time_to_render: metrics.get_time_to_render, + schemas.TemplatePredefinedKeys.avg_used_js_heap_size: metrics.get_memory_consumption, + schemas.TemplatePredefinedKeys.avg_cpu: metrics.get_avg_cpu, + schemas.TemplatePredefinedKeys.avg_fps: metrics.get_avg_fps, + schemas.TemplatePredefinedKeys.impacted_sessions_by_js_errors: metrics.get_impacted_sessions_by_js_errors, + schemas.TemplatePredefinedKeys.domains_errors_4xx: metrics.get_domains_errors_4xx, + schemas.TemplatePredefinedKeys.domains_errors_5xx: metrics.get_domains_errors_5xx, + schemas.TemplatePredefinedKeys.errors_per_domains: metrics.get_errors_per_domains, + schemas.TemplatePredefinedKeys.calls_errors: metrics.get_calls_errors, + schemas.TemplatePredefinedKeys.errors_by_type: metrics.get_errors_per_type, + schemas.TemplatePredefinedKeys.errors_by_origin: metrics.get_resources_by_party, + schemas.TemplatePredefinedKeys.speed_index_by_location: metrics.get_speed_index_location, + schemas.TemplatePredefinedKeys.slowest_domains: metrics.get_slowest_domains, + schemas.TemplatePredefinedKeys.sessions_per_browser: metrics.get_sessions_per_browser, + schemas.TemplatePredefinedKeys.time_to_render: metrics.get_time_to_render, + schemas.TemplatePredefinedKeys.impacted_sessions_by_slow_pages: metrics.get_impacted_sessions_by_slow_pages, + schemas.TemplatePredefinedKeys.memory_consumption: metrics.get_memory_consumption, + schemas.TemplatePredefinedKeys.cpu_load: metrics.get_avg_cpu, + schemas.TemplatePredefinedKeys.frame_rate: metrics.get_avg_fps, + schemas.TemplatePredefinedKeys.crashes: metrics.get_crashes, + schemas.TemplatePredefinedKeys.resources_vs_visually_complete: metrics.get_resources_vs_visually_complete, + schemas.TemplatePredefinedKeys.pages_dom_buildtime: metrics.get_pages_dom_build_time, + schemas.TemplatePredefinedKeys.pages_response_time: metrics.get_pages_response_time, + schemas.TemplatePredefinedKeys.pages_response_time_distribution: metrics.get_pages_response_time_distribution, + schemas.TemplatePredefinedKeys.missing_resources: metrics.get_missing_resources_trend, + schemas.TemplatePredefinedKeys.slowest_resources: metrics.get_slowest_resources, + schemas.TemplatePredefinedKeys.resources_fetch_time: metrics.get_resources_loading_time, + schemas.TemplatePredefinedKeys.resource_type_vs_response_end: metrics.resource_type_vs_response_end, + schemas.TemplatePredefinedKeys.resources_count_by_type: metrics.get_resources_count_by_type, } diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 3d27178cf..b167c4231 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -20,61 +20,61 @@ def get_metadata_map(projectId: int): @app.post('/{projectId}/dashboard/sessions', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/sessions', tags=["dashboard", "metrics"]) def get_dashboard_processed_sessions(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())} + return {"data": metrics.get_processed_sessions(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/errors', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/errors', tags=["dashboard", "metrics"]) def get_dashboard_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_errors(project_id=projectId, **data.dict())} + return {"data": metrics.get_errors(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/errors_trend', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/errors_trend', tags=["dashboard", "metrics"]) def get_dashboard_errors_trend(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_errors_trend(project_id=projectId, **data.dict())} + return {"data": metrics.get_errors_trend(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/application_activity', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/application_activity', tags=["dashboard", "metrics"]) def get_dashboard_application_activity(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_application_activity(project_id=projectId, **data.dict())} + return {"data": metrics.get_application_activity(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/page_metrics', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/page_metrics', tags=["dashboard", "metrics"]) def get_dashboard_page_metrics(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_page_metrics(project_id=projectId, **data.dict())} + return {"data": metrics.get_page_metrics(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/user_activity', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/user_activity', tags=["dashboard", "metrics"]) def get_dashboard_user_activity(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_user_activity(project_id=projectId, **data.dict())} + return {"data": metrics.get_user_activity(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/performance', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/performance', tags=["dashboard", "metrics"]) def get_dashboard_performance(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_performance(project_id=projectId, **data.dict())} + return {"data": metrics.get_performance(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/slowest_images', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/slowest_images', tags=["dashboard", "metrics"]) def get_dashboard_slowest_images(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_slowest_images(project_id=projectId, **data.dict())} + return {"data": metrics.get_slowest_images(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/missing_resources', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/missing_resources', tags=["dashboard", "metrics"]) def get_performance_sessions(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_missing_resources_trend(project_id=projectId, **data.dict())} + return {"data": metrics.get_missing_resources_trend(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/network', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/network', tags=["dashboard", "metrics"]) def get_network_widget(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_network(project_id=projectId, **data.dict())} + return {"data": metrics.get_network(project_id=projectId, **data.dict())} @app.get('/{projectId}/dashboard/{widget}/search', tags=["dashboard", "metrics"]) @@ -85,20 +85,20 @@ def get_dashboard_autocomplete(projectId: int, widget: str, q: str, type: str = q = '^' + q if widget in ['performance']: - data = dashboard.search(q, type, project_id=projectId, + data = metrics.search(q, type, project_id=projectId, platform=platform, performance=True) elif widget in ['pages', 'pages_dom_buildtime', 'top_metrics', 'time_to_render', 'impacted_sessions_by_slow_pages', 'pages_response_time']: - data = dashboard.search(q, type, project_id=projectId, + data = metrics.search(q, type, project_id=projectId, platform=platform, pages_only=True) elif widget in ['resources_loading_time']: - data = dashboard.search(q, type, project_id=projectId, + data = metrics.search(q, type, project_id=projectId, platform=platform, performance=False) elif widget in ['time_between_events', 'events']: - data = dashboard.search(q, type, project_id=projectId, + data = metrics.search(q, type, project_id=projectId, platform=platform, performance=False, events_only=True) elif widget in ['metadata']: - data = dashboard.search(q, None, project_id=projectId, + data = metrics.search(q, None, project_id=projectId, platform=platform, metadata=True, key=key) else: return {"errors": [f"unsupported widget: {widget}"]} @@ -109,210 +109,210 @@ def get_dashboard_autocomplete(projectId: int, widget: str, q: str, type: str = @app.post('/{projectId}/dashboard/slowest_resources', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/slowest_resources', tags=["dashboard", "metrics"]) def get_dashboard_slowest_resources(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_slowest_resources(project_id=projectId, **data.dict())} + return {"data": metrics.get_slowest_resources(project_id=projectId, **data.dict())} # 2 @app.post('/{projectId}/dashboard/resources_loading_time', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resources_loading_time', tags=["dashboard", "metrics"]) def get_dashboard_resources(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_resources_loading_time(project_id=projectId, **data.dict())} + return {"data": metrics.get_resources_loading_time(project_id=projectId, **data.dict())} # 3 @app.post('/{projectId}/dashboard/pages_dom_buildtime', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/pages_dom_buildtime', tags=["dashboard", "metrics"]) def get_dashboard_pages_dom(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())} + return {"data": metrics.get_pages_dom_build_time(project_id=projectId, **data.dict())} # 4 @app.post('/{projectId}/dashboard/busiest_time_of_day', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/busiest_time_of_day', tags=["dashboard", "metrics"]) def get_dashboard_busiest_time_of_day(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_busiest_time_of_day(project_id=projectId, **data.dict())} + return {"data": metrics.get_busiest_time_of_day(project_id=projectId, **data.dict())} # 5 @app.post('/{projectId}/dashboard/sessions_location', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/sessions_location', tags=["dashboard", "metrics"]) def get_dashboard_sessions_location(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_sessions_location(project_id=projectId, **data.dict())} + return {"data": metrics.get_sessions_location(project_id=projectId, **data.dict())} # 6 @app.post('/{projectId}/dashboard/speed_location', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/speed_location', tags=["dashboard", "metrics"]) def get_dashboard_speed_location(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_speed_index_location(project_id=projectId, **data.dict())} + return {"data": metrics.get_speed_index_location(project_id=projectId, **data.dict())} # 7 @app.post('/{projectId}/dashboard/pages_response_time', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/pages_response_time', tags=["dashboard", "metrics"]) def get_dashboard_pages_response_time(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())} + return {"data": metrics.get_pages_response_time(project_id=projectId, **data.dict())} # 8 @app.post('/{projectId}/dashboard/pages_response_time_distribution', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/pages_response_time_distribution', tags=["dashboard", "metrics"]) def get_dashboard_pages_response_time_distribution(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_pages_response_time_distribution(project_id=projectId, **data.dict())} + return {"data": metrics.get_pages_response_time_distribution(project_id=projectId, **data.dict())} # 9 @app.post('/{projectId}/dashboard/top_metrics', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/top_metrics', tags=["dashboard", "metrics"]) def get_dashboard_top_metrics(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_top_metrics(project_id=projectId, **data.dict())} + return {"data": metrics.get_top_metrics(project_id=projectId, **data.dict())} # 10 @app.post('/{projectId}/dashboard/time_to_render', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/time_to_render', tags=["dashboard", "metrics"]) def get_dashboard_time_to_render(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_time_to_render(project_id=projectId, **data.dict())} + return {"data": metrics.get_time_to_render(project_id=projectId, **data.dict())} # 11 @app.post('/{projectId}/dashboard/impacted_sessions_by_slow_pages', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/impacted_sessions_by_slow_pages', tags=["dashboard", "metrics"]) def get_dashboard_impacted_sessions_by_slow_pages(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_impacted_sessions_by_slow_pages(project_id=projectId, **data.dict())} + return {"data": metrics.get_impacted_sessions_by_slow_pages(project_id=projectId, **data.dict())} # 12 @app.post('/{projectId}/dashboard/memory_consumption', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/memory_consumption', tags=["dashboard", "metrics"]) def get_dashboard_memory_consumption(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())} + return {"data": metrics.get_memory_consumption(project_id=projectId, **data.dict())} # 12.1 @app.post('/{projectId}/dashboard/fps', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/fps', tags=["dashboard", "metrics"]) def get_dashboard_avg_fps(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + return {"data": metrics.get_avg_fps(project_id=projectId, **data.dict())} # 12.2 @app.post('/{projectId}/dashboard/cpu', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/cpu', tags=["dashboard", "metrics"]) def get_dashboard_avg_cpu(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())} + return {"data": metrics.get_avg_cpu(project_id=projectId, **data.dict())} # 13 @app.post('/{projectId}/dashboard/crashes', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/crashes', tags=["dashboard", "metrics"]) def get_dashboard_impacted_sessions_by_slow_pages(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_crashes(project_id=projectId, **data.dict())} + return {"data": metrics.get_crashes(project_id=projectId, **data.dict())} # 14 @app.post('/{projectId}/dashboard/domains_errors', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/domains_errors', tags=["dashboard", "metrics"]) def get_dashboard_domains_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_domains_errors(project_id=projectId, **data.dict())} + return {"data": metrics.get_domains_errors(project_id=projectId, **data.dict())} # 14.1 @app.post('/{projectId}/dashboard/domains_errors_4xx', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/domains_errors_4xx', tags=["dashboard", "metrics"]) def get_dashboard_domains_errors_4xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_domains_errors_4xx(project_id=projectId, **data.dict())} + return {"data": metrics.get_domains_errors_4xx(project_id=projectId, **data.dict())} # 14.2 @app.post('/{projectId}/dashboard/domains_errors_5xx', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/domains_errors_5xx', tags=["dashboard", "metrics"]) def get_dashboard_domains_errors_5xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_domains_errors_5xx(project_id=projectId, **data.dict())} + return {"data": metrics.get_domains_errors_5xx(project_id=projectId, **data.dict())} # 15 @app.post('/{projectId}/dashboard/slowest_domains', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/slowest_domains', tags=["dashboard", "metrics"]) def get_dashboard_slowest_domains(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_slowest_domains(project_id=projectId, **data.dict())} + return {"data": metrics.get_slowest_domains(project_id=projectId, **data.dict())} # 16 @app.post('/{projectId}/dashboard/errors_per_domains', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/errors_per_domains', tags=["dashboard", "metrics"]) def get_dashboard_errors_per_domains(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_errors_per_domains(project_id=projectId, **data.dict())} + return {"data": metrics.get_errors_per_domains(project_id=projectId, **data.dict())} # 17 @app.post('/{projectId}/dashboard/sessions_per_browser', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/sessions_per_browser', tags=["dashboard", "metrics"]) def get_dashboard_sessions_per_browser(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_sessions_per_browser(project_id=projectId, **data.dict())} + return {"data": metrics.get_sessions_per_browser(project_id=projectId, **data.dict())} # 18 @app.post('/{projectId}/dashboard/calls_errors', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/calls_errors', tags=["dashboard", "metrics"]) def get_dashboard_calls_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_calls_errors(project_id=projectId, **data.dict())} + return {"data": metrics.get_calls_errors(project_id=projectId, **data.dict())} # 18.1 @app.post('/{projectId}/dashboard/calls_errors_4xx', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/calls_errors_4xx', tags=["dashboard", "metrics"]) def get_dashboard_calls_errors_4xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_calls_errors_4xx(project_id=projectId, **data.dict())} + return {"data": metrics.get_calls_errors_4xx(project_id=projectId, **data.dict())} # 18.2 @app.post('/{projectId}/dashboard/calls_errors_5xx', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/calls_errors_5xx', tags=["dashboard", "metrics"]) def get_dashboard_calls_errors_5xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_calls_errors_5xx(project_id=projectId, **data.dict())} + return {"data": metrics.get_calls_errors_5xx(project_id=projectId, **data.dict())} # 19 @app.post('/{projectId}/dashboard/errors_per_type', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/errors_per_type', tags=["dashboard", "metrics"]) def get_dashboard_errors_per_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_errors_per_type(project_id=projectId, **data.dict())} + return {"data": metrics.get_errors_per_type(project_id=projectId, **data.dict())} # 20 @app.post('/{projectId}/dashboard/resources_by_party', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resources_by_party', tags=["dashboard", "metrics"]) def get_dashboard_resources_by_party(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_resources_by_party(project_id=projectId, **data.dict())} + return {"data": metrics.get_resources_by_party(project_id=projectId, **data.dict())} # 21 @app.post('/{projectId}/dashboard/resource_type_vs_response_end', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resource_type_vs_response_end', tags=["dashboard", "metrics"]) def get_dashboard_errors_per_resource_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.resource_type_vs_response_end(project_id=projectId, **data.dict())} + return {"data": metrics.resource_type_vs_response_end(project_id=projectId, **data.dict())} # 22 @app.post('/{projectId}/dashboard/resources_vs_visually_complete', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resources_vs_visually_complete', tags=["dashboard", "metrics"]) def get_dashboard_resources_vs_visually_complete(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_resources_vs_visually_complete(project_id=projectId, **data.dict())} + return {"data": metrics.get_resources_vs_visually_complete(project_id=projectId, **data.dict())} # 23 @app.post('/{projectId}/dashboard/impacted_sessions_by_js_errors', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/impacted_sessions_by_js_errors', tags=["dashboard", "metrics"]) def get_dashboard_impacted_sessions_by_js_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_impacted_sessions_by_js_errors(project_id=projectId, **data.dict())} + return {"data": metrics.get_impacted_sessions_by_js_errors(project_id=projectId, **data.dict())} # 24 @app.post('/{projectId}/dashboard/resources_count_by_type', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resources_count_by_type', tags=["dashboard", "metrics"]) def get_dashboard_resources_count_by_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_resources_count_by_type(project_id=projectId, **data.dict())} + return {"data": metrics.get_resources_count_by_type(project_id=projectId, **data.dict())} # # 25 @@ -327,23 +327,23 @@ def get_dashboard_resources_count_by_type(projectId: int, data: schemas.MetricPa def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): 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()), - "chart": dashboard.get_performance(project_id=projectId, **data.dict()) + "data": metrics.get_processed_sessions(project_id=projectId, **data.dict())}, + *helper.explode_widget(data={**metrics.get_application_activity(project_id=projectId, **data.dict()), + "chart": metrics.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=metrics.get_page_metrics(project_id=projectId, **data.dict())), + *helper.explode_widget(data=metrics.get_user_activity(project_id=projectId, **data.dict())), {"key": "avg_pages_dom_buildtime", - "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, + "data": metrics.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()) + "data": metrics.get_pages_response_time(project_id=projectId, **data.dict()) }, - *helper.explode_widget(dashboard.get_top_metrics(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())}, + *helper.explode_widget(metrics.get_top_metrics(project_id=projectId, **data.dict())), + {"key": "avg_time_to_render", "data": metrics.get_time_to_render(project_id=projectId, **data.dict())}, + {"key": "avg_used_js_heap_size", "data": metrics.get_memory_consumption(project_id=projectId, **data.dict())}, + {"key": "avg_cpu", "data": metrics.get_avg_cpu(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_fps, - "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + "data": metrics.get_avg_fps(project_id=projectId, **data.dict())} ] results = sorted(results, key=lambda r: r["key"]) return {"data": results} @@ -354,45 +354,45 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): results = [ {"key": schemas.TemplatePredefinedKeys.count_sessions, - "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, + "data": metrics.get_processed_sessions(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_image_load_time, - "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, + "data": metrics.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_page_load_time, - "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, + "data": metrics.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_request_load_time, - "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, + "data": metrics.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_dom_content_load_start, - "data": dashboard.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())}, + "data": metrics.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_first_contentful_pixel, - "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, + "data": metrics.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_visited_pages, - "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, + "data": metrics.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_session_duration, - "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, + "data": metrics.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime, - "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, + "data": metrics.get_pages_dom_build_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_pages_response_time, - "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())}, + "data": metrics.get_pages_response_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_response_time, - "data": dashboard.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_first_paint, - "data": dashboard.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_dom_content_loaded, - "data": dashboard.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_till_first_bit, - "data": dashboard.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_time_to_interactive, - "data": dashboard.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.count_requests, - "data": dashboard.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_time_to_render, - "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, + "data": metrics.get_time_to_render(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_used_js_heap_size, - "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, + "data": metrics.get_memory_consumption(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_cpu, - "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, + "data": metrics.get_avg_cpu(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_fps, - "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + "data": metrics.get_avg_fps(project_id=projectId, **data.dict())} ] results = sorted(results, key=lambda r: r["key"]) return {"data": results} diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index a5a1cede5..89bd131e5 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -12,17 +12,17 @@ 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 dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data) + return dashboards.create_dashboard(project_id=projectId, user_id=context.user_id, data=data) @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)} + return {"data": dashboards.get_dashboards(project_id=projectId, user_id=context.user_id)} @app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): - data = dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId) + data = dashboards.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} @@ -32,18 +32,18 @@ def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentCont @app.put('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) 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, + return {"data": dashboards.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) + return dashboards.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)} + return {"data": dashboards.pin_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} @app.post('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"]) @@ -51,7 +51,7 @@ def pin_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentCont 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, + return {"data": dashboards.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} @@ -60,7 +60,7 @@ def add_widget_to_dashboard(projectId: int, dashboardId: int, 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, + return {"data": dashboards.create_metric_add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} @@ -69,14 +69,14 @@ def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int, def update_widget_in_dashboard(projectId: int, dashboardId: int, widgetId: int, 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, + return dashboards.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 dashboards2.remove_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + return dashboards.remove_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, widget_id=widgetId) @@ -84,7 +84,7 @@ def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int 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, + data = dashboards.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"]} @@ -93,7 +93,7 @@ def get_widget_chart(projectId: int, dashboardId: int, widgetId: int, @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)} + return {"data": dashboards.get_templates(project_id=projectId, user_id=context.user_id)} @app.post('/{projectId}/metrics/try', tags=["dashboard"]) @@ -144,7 +144,7 @@ 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 = dashboards2.make_chart_metrics(project_id=projectId, user_id=context.user_id, metric_id=metric_id, + data = dashboards.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"]} diff --git a/ee/api/app.py b/ee/api/app.py index ed2c01aa4..0041ec12e 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.subs import v1_api from routers.crons import core_crons from routers.crons import core_dynamic_crons -from routers.subs import dashboard, insights, v1_api_ee +from routers.subs import dashboard, insights, metrics, v1_api_ee app = FastAPI() @@ -65,6 +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(metrics.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/errors.py b/ee/api/chalicelib/core/errors.py index 84beb0c30..c7e066f8b 100644 --- a/ee/api/chalicelib/core/errors.py +++ b/ee/api/chalicelib/core/errors.py @@ -82,7 +82,7 @@ def __rearrange_chart_details(start_at, end_at, density, chart): chart = list(chart) for i in range(len(chart)): chart[i] = {"timestamp": chart[i][0], "count": chart[i][1]} - chart = dashboard.__complete_missing_steps(rows=chart, start_time=start_at, end_time=end_at, density=density, + chart = metrics.__complete_missing_steps(rows=chart, start_time=start_at, end_time=end_at, density=density, neutral={"count": 0}) return chart @@ -788,7 +788,7 @@ def search_deprecated(data: schemas.SearchErrorsSchema, project_id, user_id, flo r["chart"] = list(r["chart"]) for i in range(len(r["chart"])): r["chart"][i] = {"timestamp": r["chart"][i][0], "count": r["chart"][i][1]} - r["chart"] = dashboard.__complete_missing_steps(rows=r["chart"], start_time=data.startDate, + r["chart"] = metrics.__complete_missing_steps(rows=r["chart"], start_time=data.startDate, end_time=data.endDate, density=data.density, neutral={"count": 0}) offset = len(rows) diff --git a/ee/api/chalicelib/core/projects.py b/ee/api/chalicelib/core/projects.py index 0f2b62cc9..af5e0948e 100644 --- a/ee/api/chalicelib/core/projects.py +++ b/ee/api/chalicelib/core/projects.py @@ -187,6 +187,17 @@ def edit(tenant_id, user_id, project_id, data: schemas.CreateProjectSchema): changes={"name": data.name})} +def count_by_tenant(tenant_id): + with pg_client.PostgresClient() as cur: + cur.execute(cur.mogrify("""\ + SELECT + count(s.project_id) + FROM public.projects AS s + WHERE s.deleted_at IS NULL + AND tenant_id= %(tenant_id)s;""", {"tenant_id": tenant_id})) + return cur.fetchone()["count"] + + def delete(tenant_id, user_id, project_id): admin = users.get(user_id=user_id, tenant_id=tenant_id) From 663e6aff5b5011eca4b52aab1c6fab36f202a2d3 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Tue, 19 Apr 2022 19:45:57 +0200 Subject: [PATCH 237/294] feat(api): fixes --- api/chalicelib/core/assist.py | 4 +- api/chalicelib/core/dashboards.py | 90 ++++++++--------- api/routers/subs/dashboard.py | 154 ++++++++++++++--------------- api/routers/subs/metrics.py | 26 ++--- ee/api/app.py | 3 +- ee/api/chalicelib/core/errors.py | 4 +- ee/api/chalicelib/core/projects.py | 11 +++ 7 files changed, 151 insertions(+), 141 deletions(-) diff --git a/api/chalicelib/core/assist.py b/api/chalicelib/core/assist.py index 97bff84fd..1b6e8c405 100644 --- a/api/chalicelib/core/assist.py +++ b/api/chalicelib/core/assist.py @@ -1,9 +1,7 @@ import requests from decouple import config -import schemas -from chalicelib.core import projects, sessions -from chalicelib.utils import pg_client, helper +from chalicelib.core import projects SESSION_PROJECTION_COLS = """s.project_id, s.session_id::text AS session_id, diff --git a/api/chalicelib/core/dashboards.py b/api/chalicelib/core/dashboards.py index 3af8b01a0..7b7bfe252 100644 --- a/api/chalicelib/core/dashboards.py +++ b/api/chalicelib/core/dashboards.py @@ -242,51 +242,51 @@ def create_metric_add_widget(project_id, user_id, dashboard_id, data: schemas.Cr data=schemas.AddWidgetToDashboardPayloadSchema(metricId=metric_id)) -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, - 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, - 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, - 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, +PREDEFINED = {schemas.TemplatePredefinedKeys.count_sessions: metrics.get_processed_sessions, + schemas.TemplatePredefinedKeys.avg_image_load_time: metrics.get_application_activity_avg_image_load_time, + schemas.TemplatePredefinedKeys.avg_page_load_time: metrics.get_application_activity_avg_page_load_time, + schemas.TemplatePredefinedKeys.avg_request_load_time: metrics.get_application_activity_avg_request_load_time, + schemas.TemplatePredefinedKeys.avg_dom_content_load_start: metrics.get_page_metrics_avg_dom_content_load_start, + schemas.TemplatePredefinedKeys.avg_first_contentful_pixel: metrics.get_page_metrics_avg_first_contentful_pixel, + schemas.TemplatePredefinedKeys.avg_visited_pages: metrics.get_user_activity_avg_visited_pages, + schemas.TemplatePredefinedKeys.avg_session_duration: metrics.get_user_activity_avg_session_duration, + schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime: metrics.get_pages_dom_build_time, + schemas.TemplatePredefinedKeys.avg_pages_response_time: metrics.get_pages_response_time, + schemas.TemplatePredefinedKeys.avg_response_time: metrics.get_top_metrics_avg_response_time, + schemas.TemplatePredefinedKeys.avg_first_paint: metrics.get_top_metrics_avg_first_paint, + schemas.TemplatePredefinedKeys.avg_dom_content_loaded: metrics.get_top_metrics_avg_dom_content_loaded, + schemas.TemplatePredefinedKeys.avg_till_first_bit: metrics.get_top_metrics_avg_till_first_bit, + schemas.TemplatePredefinedKeys.avg_time_to_interactive: metrics.get_top_metrics_avg_time_to_interactive, + schemas.TemplatePredefinedKeys.count_requests: metrics.get_top_metrics_count_requests, + schemas.TemplatePredefinedKeys.avg_time_to_render: metrics.get_time_to_render, + schemas.TemplatePredefinedKeys.avg_used_js_heap_size: metrics.get_memory_consumption, + schemas.TemplatePredefinedKeys.avg_cpu: metrics.get_avg_cpu, + schemas.TemplatePredefinedKeys.avg_fps: metrics.get_avg_fps, + schemas.TemplatePredefinedKeys.impacted_sessions_by_js_errors: metrics.get_impacted_sessions_by_js_errors, + schemas.TemplatePredefinedKeys.domains_errors_4xx: metrics.get_domains_errors_4xx, + schemas.TemplatePredefinedKeys.domains_errors_5xx: metrics.get_domains_errors_5xx, + schemas.TemplatePredefinedKeys.errors_per_domains: metrics.get_errors_per_domains, + schemas.TemplatePredefinedKeys.calls_errors: metrics.get_calls_errors, + schemas.TemplatePredefinedKeys.errors_by_type: metrics.get_errors_per_type, + schemas.TemplatePredefinedKeys.errors_by_origin: metrics.get_resources_by_party, + schemas.TemplatePredefinedKeys.speed_index_by_location: metrics.get_speed_index_location, + schemas.TemplatePredefinedKeys.slowest_domains: metrics.get_slowest_domains, + schemas.TemplatePredefinedKeys.sessions_per_browser: metrics.get_sessions_per_browser, + schemas.TemplatePredefinedKeys.time_to_render: metrics.get_time_to_render, + schemas.TemplatePredefinedKeys.impacted_sessions_by_slow_pages: metrics.get_impacted_sessions_by_slow_pages, + schemas.TemplatePredefinedKeys.memory_consumption: metrics.get_memory_consumption, + schemas.TemplatePredefinedKeys.cpu_load: metrics.get_avg_cpu, + schemas.TemplatePredefinedKeys.frame_rate: metrics.get_avg_fps, + schemas.TemplatePredefinedKeys.crashes: metrics.get_crashes, + schemas.TemplatePredefinedKeys.resources_vs_visually_complete: metrics.get_resources_vs_visually_complete, + schemas.TemplatePredefinedKeys.pages_dom_buildtime: metrics.get_pages_dom_build_time, + schemas.TemplatePredefinedKeys.pages_response_time: metrics.get_pages_response_time, + schemas.TemplatePredefinedKeys.pages_response_time_distribution: metrics.get_pages_response_time_distribution, + schemas.TemplatePredefinedKeys.missing_resources: metrics.get_missing_resources_trend, + schemas.TemplatePredefinedKeys.slowest_resources: metrics.get_slowest_resources, + schemas.TemplatePredefinedKeys.resources_fetch_time: metrics.get_resources_loading_time, + schemas.TemplatePredefinedKeys.resource_type_vs_response_end: metrics.resource_type_vs_response_end, + schemas.TemplatePredefinedKeys.resources_count_by_type: metrics.get_resources_count_by_type, } diff --git a/api/routers/subs/dashboard.py b/api/routers/subs/dashboard.py index 3d27178cf..b167c4231 100644 --- a/api/routers/subs/dashboard.py +++ b/api/routers/subs/dashboard.py @@ -20,61 +20,61 @@ def get_metadata_map(projectId: int): @app.post('/{projectId}/dashboard/sessions', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/sessions', tags=["dashboard", "metrics"]) def get_dashboard_processed_sessions(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())} + return {"data": metrics.get_processed_sessions(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/errors', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/errors', tags=["dashboard", "metrics"]) def get_dashboard_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_errors(project_id=projectId, **data.dict())} + return {"data": metrics.get_errors(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/errors_trend', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/errors_trend', tags=["dashboard", "metrics"]) def get_dashboard_errors_trend(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_errors_trend(project_id=projectId, **data.dict())} + return {"data": metrics.get_errors_trend(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/application_activity', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/application_activity', tags=["dashboard", "metrics"]) def get_dashboard_application_activity(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_application_activity(project_id=projectId, **data.dict())} + return {"data": metrics.get_application_activity(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/page_metrics', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/page_metrics', tags=["dashboard", "metrics"]) def get_dashboard_page_metrics(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_page_metrics(project_id=projectId, **data.dict())} + return {"data": metrics.get_page_metrics(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/user_activity', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/user_activity', tags=["dashboard", "metrics"]) def get_dashboard_user_activity(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_user_activity(project_id=projectId, **data.dict())} + return {"data": metrics.get_user_activity(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/performance', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/performance', tags=["dashboard", "metrics"]) def get_dashboard_performance(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_performance(project_id=projectId, **data.dict())} + return {"data": metrics.get_performance(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/slowest_images', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/slowest_images', tags=["dashboard", "metrics"]) def get_dashboard_slowest_images(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_slowest_images(project_id=projectId, **data.dict())} + return {"data": metrics.get_slowest_images(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/missing_resources', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/missing_resources', tags=["dashboard", "metrics"]) def get_performance_sessions(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_missing_resources_trend(project_id=projectId, **data.dict())} + return {"data": metrics.get_missing_resources_trend(project_id=projectId, **data.dict())} @app.post('/{projectId}/dashboard/network', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/network', tags=["dashboard", "metrics"]) def get_network_widget(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_network(project_id=projectId, **data.dict())} + return {"data": metrics.get_network(project_id=projectId, **data.dict())} @app.get('/{projectId}/dashboard/{widget}/search', tags=["dashboard", "metrics"]) @@ -85,20 +85,20 @@ def get_dashboard_autocomplete(projectId: int, widget: str, q: str, type: str = q = '^' + q if widget in ['performance']: - data = dashboard.search(q, type, project_id=projectId, + data = metrics.search(q, type, project_id=projectId, platform=platform, performance=True) elif widget in ['pages', 'pages_dom_buildtime', 'top_metrics', 'time_to_render', 'impacted_sessions_by_slow_pages', 'pages_response_time']: - data = dashboard.search(q, type, project_id=projectId, + data = metrics.search(q, type, project_id=projectId, platform=platform, pages_only=True) elif widget in ['resources_loading_time']: - data = dashboard.search(q, type, project_id=projectId, + data = metrics.search(q, type, project_id=projectId, platform=platform, performance=False) elif widget in ['time_between_events', 'events']: - data = dashboard.search(q, type, project_id=projectId, + data = metrics.search(q, type, project_id=projectId, platform=platform, performance=False, events_only=True) elif widget in ['metadata']: - data = dashboard.search(q, None, project_id=projectId, + data = metrics.search(q, None, project_id=projectId, platform=platform, metadata=True, key=key) else: return {"errors": [f"unsupported widget: {widget}"]} @@ -109,210 +109,210 @@ def get_dashboard_autocomplete(projectId: int, widget: str, q: str, type: str = @app.post('/{projectId}/dashboard/slowest_resources', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/slowest_resources', tags=["dashboard", "metrics"]) def get_dashboard_slowest_resources(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_slowest_resources(project_id=projectId, **data.dict())} + return {"data": metrics.get_slowest_resources(project_id=projectId, **data.dict())} # 2 @app.post('/{projectId}/dashboard/resources_loading_time', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resources_loading_time', tags=["dashboard", "metrics"]) def get_dashboard_resources(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_resources_loading_time(project_id=projectId, **data.dict())} + return {"data": metrics.get_resources_loading_time(project_id=projectId, **data.dict())} # 3 @app.post('/{projectId}/dashboard/pages_dom_buildtime', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/pages_dom_buildtime', tags=["dashboard", "metrics"]) def get_dashboard_pages_dom(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())} + return {"data": metrics.get_pages_dom_build_time(project_id=projectId, **data.dict())} # 4 @app.post('/{projectId}/dashboard/busiest_time_of_day', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/busiest_time_of_day', tags=["dashboard", "metrics"]) def get_dashboard_busiest_time_of_day(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_busiest_time_of_day(project_id=projectId, **data.dict())} + return {"data": metrics.get_busiest_time_of_day(project_id=projectId, **data.dict())} # 5 @app.post('/{projectId}/dashboard/sessions_location', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/sessions_location', tags=["dashboard", "metrics"]) def get_dashboard_sessions_location(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_sessions_location(project_id=projectId, **data.dict())} + return {"data": metrics.get_sessions_location(project_id=projectId, **data.dict())} # 6 @app.post('/{projectId}/dashboard/speed_location', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/speed_location', tags=["dashboard", "metrics"]) def get_dashboard_speed_location(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_speed_index_location(project_id=projectId, **data.dict())} + return {"data": metrics.get_speed_index_location(project_id=projectId, **data.dict())} # 7 @app.post('/{projectId}/dashboard/pages_response_time', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/pages_response_time', tags=["dashboard", "metrics"]) def get_dashboard_pages_response_time(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())} + return {"data": metrics.get_pages_response_time(project_id=projectId, **data.dict())} # 8 @app.post('/{projectId}/dashboard/pages_response_time_distribution', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/pages_response_time_distribution', tags=["dashboard", "metrics"]) def get_dashboard_pages_response_time_distribution(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_pages_response_time_distribution(project_id=projectId, **data.dict())} + return {"data": metrics.get_pages_response_time_distribution(project_id=projectId, **data.dict())} # 9 @app.post('/{projectId}/dashboard/top_metrics', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/top_metrics', tags=["dashboard", "metrics"]) def get_dashboard_top_metrics(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_top_metrics(project_id=projectId, **data.dict())} + return {"data": metrics.get_top_metrics(project_id=projectId, **data.dict())} # 10 @app.post('/{projectId}/dashboard/time_to_render', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/time_to_render', tags=["dashboard", "metrics"]) def get_dashboard_time_to_render(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_time_to_render(project_id=projectId, **data.dict())} + return {"data": metrics.get_time_to_render(project_id=projectId, **data.dict())} # 11 @app.post('/{projectId}/dashboard/impacted_sessions_by_slow_pages', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/impacted_sessions_by_slow_pages', tags=["dashboard", "metrics"]) def get_dashboard_impacted_sessions_by_slow_pages(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_impacted_sessions_by_slow_pages(project_id=projectId, **data.dict())} + return {"data": metrics.get_impacted_sessions_by_slow_pages(project_id=projectId, **data.dict())} # 12 @app.post('/{projectId}/dashboard/memory_consumption', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/memory_consumption', tags=["dashboard", "metrics"]) def get_dashboard_memory_consumption(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())} + return {"data": metrics.get_memory_consumption(project_id=projectId, **data.dict())} # 12.1 @app.post('/{projectId}/dashboard/fps', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/fps', tags=["dashboard", "metrics"]) def get_dashboard_avg_fps(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + return {"data": metrics.get_avg_fps(project_id=projectId, **data.dict())} # 12.2 @app.post('/{projectId}/dashboard/cpu', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/cpu', tags=["dashboard", "metrics"]) def get_dashboard_avg_cpu(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())} + return {"data": metrics.get_avg_cpu(project_id=projectId, **data.dict())} # 13 @app.post('/{projectId}/dashboard/crashes', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/crashes', tags=["dashboard", "metrics"]) def get_dashboard_impacted_sessions_by_slow_pages(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_crashes(project_id=projectId, **data.dict())} + return {"data": metrics.get_crashes(project_id=projectId, **data.dict())} # 14 @app.post('/{projectId}/dashboard/domains_errors', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/domains_errors', tags=["dashboard", "metrics"]) def get_dashboard_domains_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_domains_errors(project_id=projectId, **data.dict())} + return {"data": metrics.get_domains_errors(project_id=projectId, **data.dict())} # 14.1 @app.post('/{projectId}/dashboard/domains_errors_4xx', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/domains_errors_4xx', tags=["dashboard", "metrics"]) def get_dashboard_domains_errors_4xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_domains_errors_4xx(project_id=projectId, **data.dict())} + return {"data": metrics.get_domains_errors_4xx(project_id=projectId, **data.dict())} # 14.2 @app.post('/{projectId}/dashboard/domains_errors_5xx', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/domains_errors_5xx', tags=["dashboard", "metrics"]) def get_dashboard_domains_errors_5xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_domains_errors_5xx(project_id=projectId, **data.dict())} + return {"data": metrics.get_domains_errors_5xx(project_id=projectId, **data.dict())} # 15 @app.post('/{projectId}/dashboard/slowest_domains', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/slowest_domains', tags=["dashboard", "metrics"]) def get_dashboard_slowest_domains(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_slowest_domains(project_id=projectId, **data.dict())} + return {"data": metrics.get_slowest_domains(project_id=projectId, **data.dict())} # 16 @app.post('/{projectId}/dashboard/errors_per_domains', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/errors_per_domains', tags=["dashboard", "metrics"]) def get_dashboard_errors_per_domains(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_errors_per_domains(project_id=projectId, **data.dict())} + return {"data": metrics.get_errors_per_domains(project_id=projectId, **data.dict())} # 17 @app.post('/{projectId}/dashboard/sessions_per_browser', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/sessions_per_browser', tags=["dashboard", "metrics"]) def get_dashboard_sessions_per_browser(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_sessions_per_browser(project_id=projectId, **data.dict())} + return {"data": metrics.get_sessions_per_browser(project_id=projectId, **data.dict())} # 18 @app.post('/{projectId}/dashboard/calls_errors', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/calls_errors', tags=["dashboard", "metrics"]) def get_dashboard_calls_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_calls_errors(project_id=projectId, **data.dict())} + return {"data": metrics.get_calls_errors(project_id=projectId, **data.dict())} # 18.1 @app.post('/{projectId}/dashboard/calls_errors_4xx', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/calls_errors_4xx', tags=["dashboard", "metrics"]) def get_dashboard_calls_errors_4xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_calls_errors_4xx(project_id=projectId, **data.dict())} + return {"data": metrics.get_calls_errors_4xx(project_id=projectId, **data.dict())} # 18.2 @app.post('/{projectId}/dashboard/calls_errors_5xx', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/calls_errors_5xx', tags=["dashboard", "metrics"]) def get_dashboard_calls_errors_5xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_calls_errors_5xx(project_id=projectId, **data.dict())} + return {"data": metrics.get_calls_errors_5xx(project_id=projectId, **data.dict())} # 19 @app.post('/{projectId}/dashboard/errors_per_type', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/errors_per_type', tags=["dashboard", "metrics"]) def get_dashboard_errors_per_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_errors_per_type(project_id=projectId, **data.dict())} + return {"data": metrics.get_errors_per_type(project_id=projectId, **data.dict())} # 20 @app.post('/{projectId}/dashboard/resources_by_party', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resources_by_party', tags=["dashboard", "metrics"]) def get_dashboard_resources_by_party(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_resources_by_party(project_id=projectId, **data.dict())} + return {"data": metrics.get_resources_by_party(project_id=projectId, **data.dict())} # 21 @app.post('/{projectId}/dashboard/resource_type_vs_response_end', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resource_type_vs_response_end', tags=["dashboard", "metrics"]) def get_dashboard_errors_per_resource_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.resource_type_vs_response_end(project_id=projectId, **data.dict())} + return {"data": metrics.resource_type_vs_response_end(project_id=projectId, **data.dict())} # 22 @app.post('/{projectId}/dashboard/resources_vs_visually_complete', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resources_vs_visually_complete', tags=["dashboard", "metrics"]) def get_dashboard_resources_vs_visually_complete(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_resources_vs_visually_complete(project_id=projectId, **data.dict())} + return {"data": metrics.get_resources_vs_visually_complete(project_id=projectId, **data.dict())} # 23 @app.post('/{projectId}/dashboard/impacted_sessions_by_js_errors', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/impacted_sessions_by_js_errors', tags=["dashboard", "metrics"]) def get_dashboard_impacted_sessions_by_js_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_impacted_sessions_by_js_errors(project_id=projectId, **data.dict())} + return {"data": metrics.get_impacted_sessions_by_js_errors(project_id=projectId, **data.dict())} # 24 @app.post('/{projectId}/dashboard/resources_count_by_type', tags=["dashboard", "metrics"]) @app.get('/{projectId}/dashboard/resources_count_by_type', tags=["dashboard", "metrics"]) def get_dashboard_resources_count_by_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): - return {"data": dashboard.get_resources_count_by_type(project_id=projectId, **data.dict())} + return {"data": metrics.get_resources_count_by_type(project_id=projectId, **data.dict())} # # 25 @@ -327,23 +327,23 @@ def get_dashboard_resources_count_by_type(projectId: int, data: schemas.MetricPa def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): 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()), - "chart": dashboard.get_performance(project_id=projectId, **data.dict()) + "data": metrics.get_processed_sessions(project_id=projectId, **data.dict())}, + *helper.explode_widget(data={**metrics.get_application_activity(project_id=projectId, **data.dict()), + "chart": metrics.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=metrics.get_page_metrics(project_id=projectId, **data.dict())), + *helper.explode_widget(data=metrics.get_user_activity(project_id=projectId, **data.dict())), {"key": "avg_pages_dom_buildtime", - "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, + "data": metrics.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()) + "data": metrics.get_pages_response_time(project_id=projectId, **data.dict()) }, - *helper.explode_widget(dashboard.get_top_metrics(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())}, + *helper.explode_widget(metrics.get_top_metrics(project_id=projectId, **data.dict())), + {"key": "avg_time_to_render", "data": metrics.get_time_to_render(project_id=projectId, **data.dict())}, + {"key": "avg_used_js_heap_size", "data": metrics.get_memory_consumption(project_id=projectId, **data.dict())}, + {"key": "avg_cpu", "data": metrics.get_avg_cpu(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_fps, - "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + "data": metrics.get_avg_fps(project_id=projectId, **data.dict())} ] results = sorted(results, key=lambda r: r["key"]) return {"data": results} @@ -354,45 +354,45 @@ def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)): results = [ {"key": schemas.TemplatePredefinedKeys.count_sessions, - "data": dashboard.get_processed_sessions(project_id=projectId, **data.dict())}, + "data": metrics.get_processed_sessions(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_image_load_time, - "data": dashboard.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, + "data": metrics.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_page_load_time, - "data": dashboard.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, + "data": metrics.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_request_load_time, - "data": dashboard.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, + "data": metrics.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_dom_content_load_start, - "data": dashboard.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())}, + "data": metrics.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_first_contentful_pixel, - "data": dashboard.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, + "data": metrics.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_visited_pages, - "data": dashboard.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, + "data": metrics.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_session_duration, - "data": dashboard.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, + "data": metrics.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime, - "data": dashboard.get_pages_dom_build_time(project_id=projectId, **data.dict())}, + "data": metrics.get_pages_dom_build_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_pages_response_time, - "data": dashboard.get_pages_response_time(project_id=projectId, **data.dict())}, + "data": metrics.get_pages_response_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_response_time, - "data": dashboard.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_first_paint, - "data": dashboard.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_dom_content_loaded, - "data": dashboard.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_till_first_bit, - "data": dashboard.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_time_to_interactive, - "data": dashboard.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.count_requests, - "data": dashboard.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, + "data": metrics.get_top_metrics_count_requests(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_time_to_render, - "data": dashboard.get_time_to_render(project_id=projectId, **data.dict())}, + "data": metrics.get_time_to_render(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_used_js_heap_size, - "data": dashboard.get_memory_consumption(project_id=projectId, **data.dict())}, + "data": metrics.get_memory_consumption(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_cpu, - "data": dashboard.get_avg_cpu(project_id=projectId, **data.dict())}, + "data": metrics.get_avg_cpu(project_id=projectId, **data.dict())}, {"key": schemas.TemplatePredefinedKeys.avg_fps, - "data": dashboard.get_avg_fps(project_id=projectId, **data.dict())} + "data": metrics.get_avg_fps(project_id=projectId, **data.dict())} ] results = sorted(results, key=lambda r: r["key"]) return {"data": results} diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index a5a1cede5..89bd131e5 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -12,17 +12,17 @@ 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 dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data) + return dashboards.create_dashboard(project_id=projectId, user_id=context.user_id, data=data) @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)} + return {"data": dashboards.get_dashboards(project_id=projectId, user_id=context.user_id)} @app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): - data = dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId) + data = dashboards.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} @@ -32,18 +32,18 @@ def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentCont @app.put('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) 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, + return {"data": dashboards.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) + return dashboards.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)} + return {"data": dashboards.pin_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} @app.post('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"]) @@ -51,7 +51,7 @@ def pin_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentCont 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, + return {"data": dashboards.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} @@ -60,7 +60,7 @@ def add_widget_to_dashboard(projectId: int, dashboardId: int, 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, + return {"data": dashboards.create_metric_add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} @@ -69,14 +69,14 @@ def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int, def update_widget_in_dashboard(projectId: int, dashboardId: int, widgetId: int, 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, + return dashboards.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 dashboards2.remove_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + return dashboards.remove_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, widget_id=widgetId) @@ -84,7 +84,7 @@ def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int 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, + data = dashboards.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"]} @@ -93,7 +93,7 @@ def get_widget_chart(projectId: int, dashboardId: int, widgetId: int, @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)} + return {"data": dashboards.get_templates(project_id=projectId, user_id=context.user_id)} @app.post('/{projectId}/metrics/try', tags=["dashboard"]) @@ -144,7 +144,7 @@ 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 = dashboards2.make_chart_metrics(project_id=projectId, user_id=context.user_id, metric_id=metric_id, + data = dashboards.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"]} diff --git a/ee/api/app.py b/ee/api/app.py index ed2c01aa4..0041ec12e 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.subs import v1_api from routers.crons import core_crons from routers.crons import core_dynamic_crons -from routers.subs import dashboard, insights, v1_api_ee +from routers.subs import dashboard, insights, metrics, v1_api_ee app = FastAPI() @@ -65,6 +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(metrics.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/errors.py b/ee/api/chalicelib/core/errors.py index 84beb0c30..c7e066f8b 100644 --- a/ee/api/chalicelib/core/errors.py +++ b/ee/api/chalicelib/core/errors.py @@ -82,7 +82,7 @@ def __rearrange_chart_details(start_at, end_at, density, chart): chart = list(chart) for i in range(len(chart)): chart[i] = {"timestamp": chart[i][0], "count": chart[i][1]} - chart = dashboard.__complete_missing_steps(rows=chart, start_time=start_at, end_time=end_at, density=density, + chart = metrics.__complete_missing_steps(rows=chart, start_time=start_at, end_time=end_at, density=density, neutral={"count": 0}) return chart @@ -788,7 +788,7 @@ def search_deprecated(data: schemas.SearchErrorsSchema, project_id, user_id, flo r["chart"] = list(r["chart"]) for i in range(len(r["chart"])): r["chart"][i] = {"timestamp": r["chart"][i][0], "count": r["chart"][i][1]} - r["chart"] = dashboard.__complete_missing_steps(rows=r["chart"], start_time=data.startDate, + r["chart"] = metrics.__complete_missing_steps(rows=r["chart"], start_time=data.startDate, end_time=data.endDate, density=data.density, neutral={"count": 0}) offset = len(rows) diff --git a/ee/api/chalicelib/core/projects.py b/ee/api/chalicelib/core/projects.py index 0f2b62cc9..af5e0948e 100644 --- a/ee/api/chalicelib/core/projects.py +++ b/ee/api/chalicelib/core/projects.py @@ -187,6 +187,17 @@ def edit(tenant_id, user_id, project_id, data: schemas.CreateProjectSchema): changes={"name": data.name})} +def count_by_tenant(tenant_id): + with pg_client.PostgresClient() as cur: + cur.execute(cur.mogrify("""\ + SELECT + count(s.project_id) + FROM public.projects AS s + WHERE s.deleted_at IS NULL + AND tenant_id= %(tenant_id)s;""", {"tenant_id": tenant_id})) + return cur.fetchone()["count"] + + def delete(tenant_id, user_id, project_id): admin = users.get(user_id=user_id, tenant_id=tenant_id) From bf0c10166f81294d2f0984f16254b7363c4deb4f Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 20 Apr 2022 11:04:01 +0200 Subject: [PATCH 238/294] feat(api): EE get save_request_payloads state --- ee/api/chalicelib/core/projects.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ee/api/chalicelib/core/projects.py b/ee/api/chalicelib/core/projects.py index af5e0948e..af6decffd 100644 --- a/ee/api/chalicelib/core/projects.py +++ b/ee/api/chalicelib/core/projects.py @@ -66,7 +66,7 @@ def get_projects(tenant_id, recording_state=False, gdpr=None, recorded=False, st cur.execute( cur.mogrify(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 ''} @@ -121,7 +121,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 2d7f6a3a5a7b31c20e5cc7e43d51307e4c22e093 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Apr 2022 13:22:47 +0200 Subject: [PATCH 239/294] change(ui) - filters text change --- frontend/app/constants/filterOptions.js | 4 ++++ frontend/app/types/filter/newFilter.js | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/app/constants/filterOptions.js b/frontend/app/constants/filterOptions.js index 861c61faf..28b29d0a9 100644 --- a/frontend/app/constants/filterOptions.js +++ b/frontend/app/constants/filterOptions.js @@ -6,6 +6,7 @@ export const options = [ { key: 'onAny', text: 'on any', value: 'onAny' }, { key: 'is', text: 'is', value: 'is' }, { key: 'isAny', text: 'is any', value: 'isAny' }, + { key: 'isAnyPage', text: 'is any page', value: 'isAny' }, { key: 'isNot', text: 'is not', value: 'isNot' }, { key: 'startsWith', text: 'starts with', value: 'startsWith' }, { key: 'endsWith', text: 'ends with', value: 'endsWith' }, @@ -32,6 +33,7 @@ export const options = [ const filterKeys = ['is', 'isNot']; const stringFilterKeys = ['is', 'isAny', 'isNot', 'contains', 'startsWith', 'endsWith', 'notContains']; +const stringFilterKeysPerformance = ['is', 'isAnyPage', 'isNot', 'contains', 'startsWith', 'endsWith', 'notContains']; const targetFilterKeys = ['on', 'notOn', 'onAny', 'contains', 'startsWith', 'endsWith', 'notContains']; const signUpStatusFilterKeys = ['isSignedUp', 'notSignedUp']; const rangeFilterKeys = ['before', 'after', 'on', 'inRange', 'notInRange', 'withInLast', 'notWithInLast']; @@ -42,6 +44,7 @@ const getOperatorsByKeys = (keys) => { export const baseOperators = options.filter(({key}) => filterKeys.includes(key)); export const stringOperators = options.filter(({key}) => stringFilterKeys.includes(key)); +export const stringOperatorsPerformance = options.filter(({key}) => stringFilterKeysPerformance.includes(key)); export const targetOperators = options.filter(({key}) => targetFilterKeys.includes(key)); export const booleanOperators = [ { key: 'true', text: 'true', value: 'true' }, @@ -114,6 +117,7 @@ export default { targetOperators, booleanOperators, customOperators, + stringOperatorsPerformance, getOperatorsByKeys, metricTypes, metricOf, diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index c7f4934f6..22a27f144 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -46,12 +46,12 @@ export const filtersMap = { [FilterKey.USERANONYMOUSID]: { key: FilterKey.USERANONYMOUSID, type: FilterType.MULTIPLE, category: FilterCategory.USER, label: 'User AnonymousId', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/userid' }, // PERFORMANCE - [FilterKey.DOM_COMPLETE]: { key: FilterKey.DOM_COMPLETE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'DOM Complete', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/dom-complete', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, - [FilterKey.LARGEST_CONTENTFUL_PAINT_TIME]: { key: FilterKey.LARGEST_CONTENTFUL_PAINT_TIME, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Largest Contentful Paint', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/lcpt', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, - [FilterKey.TTFB]: { key: FilterKey.TTFB, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Time to First Byte', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/ttfb', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, - [FilterKey.AVG_CPU_LOAD]: { key: FilterKey.AVG_CPU_LOAD, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg CPU Load', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/cpu-load', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, - [FilterKey.AVG_MEMORY_USAGE]: { key: FilterKey.AVG_MEMORY_USAGE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg Memory Usage', operator: 'isAny', operatorOptions: filterOptions.stringOperators, source: [], icon: 'filters/memory-load', isEvent: true, hasSource: true, sourceOperator: '=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, - [FilterKey.FETCH_FAILED]: { key: FilterKey.FETCH_FAILED, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Failed Request', operator: 'isAny', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch-failed', isEvent: true }, + [FilterKey.DOM_COMPLETE]: { key: FilterKey.DOM_COMPLETE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'DOM Complete', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/dom-complete', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + [FilterKey.LARGEST_CONTENTFUL_PAINT_TIME]: { key: FilterKey.LARGEST_CONTENTFUL_PAINT_TIME, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Largest Contentful Paint', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/lcpt', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + [FilterKey.TTFB]: { key: FilterKey.TTFB, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Time to First Byte', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/ttfb', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + [FilterKey.AVG_CPU_LOAD]: { key: FilterKey.AVG_CPU_LOAD, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg CPU Load', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/cpu-load', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + [FilterKey.AVG_MEMORY_USAGE]: { key: FilterKey.AVG_MEMORY_USAGE, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Avg Memory Usage', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, source: [], icon: 'filters/memory-load', isEvent: true, hasSource: true, sourceOperator: '>=', sourceType: FilterType.NUMBER, sourceOperatorOptions: filterOptions.customOperators }, + [FilterKey.FETCH_FAILED]: { key: FilterKey.FETCH_FAILED, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'Failed Request', operator: 'isAny', operatorOptions: filterOptions.stringOperatorsPerformance, icon: 'filters/fetch-failed', isEvent: true }, [FilterKey.ISSUE]: { key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/click', options: filterOptions.issueOptions }, } From f18522f136b6635b5915dab2a9f917b959f7088e Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Apr 2022 13:48:17 +0200 Subject: [PATCH 240/294] change(ui) - filters show request or response body based on the flag from the api --- .../components/shared/Filters/FilterItem/FilterItem.tsx | 7 ++++--- .../components/shared/Filters/FilterList/FilterList.tsx | 4 +++- .../app/components/shared/SessionSearch/SessionSearch.tsx | 5 ++++- frontend/app/duck/site.js | 8 ++++++-- frontend/app/types/site/site.js | 1 + 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx b/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx index 6a3829699..579a74231 100644 --- a/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx +++ b/frontend/app/components/shared/Filters/FilterItem/FilterItem.tsx @@ -4,7 +4,7 @@ import FilterSelection from '../FilterSelection'; import FilterValue from '../FilterValue'; import { Icon } from 'UI'; import FilterSource from '../FilterSource'; -import { FilterType } from 'App/types/filter/filterType'; +import { FilterKey, FilterType } from 'App/types/filter/filterType'; import SubFilterItem from '../SubFilterItem'; interface Props { @@ -13,9 +13,10 @@ interface Props { onUpdate: (filter) => void; onRemoveFilter: () => void; isFilter?: boolean; + saveRequestPayloads?: boolean; } function FilterItem(props: Props) { - const { isFilter = false, filterIndex, filter } = props; + const { isFilter = false, filterIndex, filter, saveRequestPayloads } = props; const canShowValues = !(filter.operator === "isAny" || filter.operator === "onAny" || filter.operator === "isUndefined"); const isSubFilter = filter.type === FilterType.SUB_FILTERS; @@ -83,7 +84,7 @@ function FilterItem(props: Props) { {/* filters */} {isSubFilter && (
- {filter.filters.map((subFilter, subFilterIndex) => ( + {filter.filters.filter(i => (i.key !== FilterKey.FETCH_REQUEST_BODY && i.key !== FilterKey.FETCH_RESPONSE_BODY) || saveRequestPayloads).map((subFilter, subFilterIndex) => ( void; hideEventsOrder?: boolean; observeChanges?: () => void; + saveRequestPayloads?: boolean; } function FilterList(props: Props) { - const { observeChanges = () => {}, filter, hideEventsOrder = false } = props; + const { observeChanges = () => {}, filter, hideEventsOrder = false, saveRequestPayloads } = 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; @@ -66,6 +67,7 @@ function FilterList(props: Props) { filter={filter} onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)} onRemoveFilter={() => onRemoveFilter(filterIndex) } + saveRequestPayloads={saveRequestPayloads} /> ): null)}
diff --git a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx index 17904c1ba..8520af60e 100644 --- a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx +++ b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx @@ -11,9 +11,10 @@ interface Props { appliedFilter: any; edit: typeof edit; addFilter: typeof addFilter; + saveRequestPayloads: boolean; } function SessionSearch(props: Props) { - const { appliedFilter } = props; + const { appliedFilter, saveRequestPayloads = false } = props; const hasEvents = appliedFilter.filters.filter(i => i.isEvent).size > 0; const hasFilters = appliedFilter.filters.filter(i => !i.isEvent).size > 0; @@ -60,6 +61,7 @@ function SessionSearch(props: Props) { onUpdateFilter={onUpdateFilter} onRemoveFilter={onRemoveFilter} onChangeEventsOrder={onChangeEventsOrder} + saveRequestPayloads={saveRequestPayloads} />
@@ -82,5 +84,6 @@ function SessionSearch(props: Props) { } export default connect(state => ({ + saveRequestPayloads: state.getIn(['site', 'active', 'saveRequestPayloads']), appliedFilter: state.getIn([ 'search', 'instance' ]), }), { edit, addFilter })(SessionSearch); \ No newline at end of file diff --git a/frontend/app/duck/site.js b/frontend/app/duck/site.js index ff5142804..f8be1a89f 100644 --- a/frontend/app/duck/site.js +++ b/frontend/app/duck/site.js @@ -40,6 +40,7 @@ const initialState = Map({ instance: fromJS(), remainingSites: undefined, siteId: null, + active: null, }); const reducer = (state = initialState, action = {}) => { @@ -59,10 +60,13 @@ const reducer = (state = initialState, action = {}) => { ? storedSiteId : action.data[0].projectId; } - return state.set('list', List(action.data.map(Site))).set('siteId', siteId); + return state.set('list', List(action.data.map(Site))) + .set('siteId', siteId) + .set('active', List(action.data.map(Site)).find(s => s.id === parseInt(siteId))); case SET_SITE_ID: localStorage.setItem(SITE_ID_STORAGE_KEY, action.siteId) - return state.set('siteId', action.siteId); + const site = state.get('list').find(s => s.id === action.siteId); + return state.set('siteId', action.siteId).set('active', site); } return state; }; diff --git a/frontend/app/types/site/site.js b/frontend/app/types/site/site.js index df76e74cd..11e48f1d7 100644 --- a/frontend/app/types/site/site.js +++ b/frontend/app/types/site/site.js @@ -22,6 +22,7 @@ export default Record({ stackIntegrations: false, projectKey: undefined, trackerVersion: undefined, + saveRequestPayloads: false, }, { idKey: 'id', methods: { From 4c90f2329e746ba2d7592e10638c875990f64407 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Apr 2022 14:09:49 +0200 Subject: [PATCH 241/294] fix(ui) - autocomplete request params --- .../shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx index 62ec51c9d..a97f3573c 100644 --- a/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx +++ b/frontend/app/components/shared/Filters/FilterAutoComplete/FilterAutoComplete.tsx @@ -60,7 +60,7 @@ function FilterAutoComplete(props: Props) { .finally(() => setLoading(false)); } - const debouncedRequestValues = React.useCallback(debounce(requestValues, 1000), []); + const debouncedRequestValues = React.useCallback(debounce(requestValues, 1000), [params]); const onInputChange = ({ target: { value } }) => { setQuery(value); From 438b68bc88f8f00bba19a342ded33d93386a61fa Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Apr 2022 14:12:14 +0200 Subject: [PATCH 242/294] fix(ui) - filter option text change --- frontend/app/constants/filterOptions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/constants/filterOptions.js b/frontend/app/constants/filterOptions.js index 28b29d0a9..d584a7b24 100644 --- a/frontend/app/constants/filterOptions.js +++ b/frontend/app/constants/filterOptions.js @@ -6,7 +6,7 @@ export const options = [ { key: 'onAny', text: 'on any', value: 'onAny' }, { key: 'is', text: 'is', value: 'is' }, { key: 'isAny', text: 'is any', value: 'isAny' }, - { key: 'isAnyPage', text: 'is any page', value: 'isAny' }, + { key: 'inAnyPage', text: 'in any page', value: 'isAny' }, { key: 'isNot', text: 'is not', value: 'isNot' }, { key: 'startsWith', text: 'starts with', value: 'startsWith' }, { key: 'endsWith', text: 'ends with', value: 'endsWith' }, @@ -33,7 +33,7 @@ export const options = [ const filterKeys = ['is', 'isNot']; const stringFilterKeys = ['is', 'isAny', 'isNot', 'contains', 'startsWith', 'endsWith', 'notContains']; -const stringFilterKeysPerformance = ['is', 'isAnyPage', 'isNot', 'contains', 'startsWith', 'endsWith', 'notContains']; +const stringFilterKeysPerformance = ['is', 'inAnyPage', 'isNot', 'contains', 'startsWith', 'endsWith', 'notContains']; const targetFilterKeys = ['on', 'notOn', 'onAny', 'contains', 'startsWith', 'endsWith', 'notContains']; const signUpStatusFilterKeys = ['isSignedUp', 'notSignedUp']; const rangeFilterKeys = ['before', 'after', 'on', 'inRange', 'notInRange', 'withInLast', 'notWithInLast']; From 27cfbefd96b2bf80ea177a8181a04c72069cb6c6 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Apr 2022 15:05:07 +0200 Subject: [PATCH 243/294] fix(ui) - filters array check --- frontend/app/mstore/types/filter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/mstore/types/filter.ts b/frontend/app/mstore/types/filter.ts index 3c4e0aa1a..900008fa4 100644 --- a/frontend/app/mstore/types/filter.ts +++ b/frontend/app/mstore/types/filter.ts @@ -51,7 +51,7 @@ export default class Filter implements IFilter { addFilter(filter: any) { filter.value = [""] - if (filter.hasOwnProperty('filters')) { + if (Array.isArray(filter.filters)) { filter.filters = filter.filters.map(i => { i.value = [""] return new FilterItem(i) From c84d3b483261fe7735b4b403e6da42007b8861b2 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 20 Apr 2022 15:23:54 +0200 Subject: [PATCH 244/294] feat(api): custom metrics try sessions endpoint --- api/chalicelib/core/custom_metrics.py | 13 +++++++++++++ api/routers/subs/metrics.py | 23 ++++++++++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 2ecc466c6..9c3f4bffb 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -105,6 +105,19 @@ def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessi return results +def try_sessions(project_id, user_id, data: schemas.TryCustomMetricsPayloadSchema): + results = [] + for s in data.series: + s.filter.startDate = data.startTimestamp + s.filter.endDate = data.endTimestamp + s.filter.limit = data.limit + s.filter.page = data.page + results.append({"seriesId": None, "seriesName": s.name, + **sessions.search2_pg(data=s.filter, project_id=project_id, user_id=user_id)}) + + return results + + def create(project_id, user_id, data: schemas.CreateCustomMetricsSchema, dashboard=False): with pg_client.PostgresClient() as cur: _data = {} diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 89bd131e5..86e9d0064 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -33,7 +33,7 @@ def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentCont def update_dashboard(projectId: int, dashboardId: int, data: schemas.EditDashboardSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards.update_dashboard(project_id=projectId, user_id=context.user_id, - dashboard_id=dashboardId, data=data)} + dashboard_id=dashboardId, data=data)} @app.delete('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"]) @@ -52,7 +52,7 @@ def add_widget_to_dashboard(projectId: int, dashboardId: int, data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, - data=data)} + data=data)} @app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"]) @@ -61,7 +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": dashboards.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"]) @@ -70,14 +70,14 @@ def update_widget_in_dashboard(projectId: int, dashboardId: int, widgetId: int, data: schemas.UpdateWidgetPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): return dashboards.update_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, - widget_id=widgetId, data=data) + 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 dashboards.remove_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, - widget_id=widgetId) + widget_id=widgetId) @app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}/chart', tags=["dashboard"]) @@ -85,7 +85,7 @@ def get_widget_chart(projectId: int, dashboardId: int, widgetId: int, data: schemas.CustomMetricChartPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): data = dashboards.make_chart_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, - widget_id=widgetId, data=data) + widget_id=widgetId, data=data) if data is None: return {"errors": ["widget not found"]} return {"data": data} @@ -105,6 +105,15 @@ def try_custom_metric(projectId: int, data: schemas.TryCustomMetricsPayloadSchem return {"data": custom_metrics.merged_live(project_id=projectId, data=data)} +@app.post('/{projectId}/metrics/try/sessions', tags=["dashboard"]) +@app.post('/{projectId}/custom_metrics/try/sessions', tags=["customMetrics"]) +def try_custom_metric_sessions(projectId: int, + data: schemas.TryCustomMetricsPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + data = custom_metrics.try_sessions(project_id=projectId, user_id=context.user_id, data=data) + return {"data": data} + + @app.post('/{projectId}/metrics', tags=["dashboard"]) @app.put('/{projectId}/metrics', tags=["dashboard"]) @app.post('/{projectId}/custom_metrics', tags=["customMetrics"]) @@ -145,7 +154,7 @@ def get_custom_metric_sessions(projectId: int, metric_id: int, def get_custom_metric_chart(projectId: int, metric_id: int, data: schemas.CustomMetricChartPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): data = dashboards.make_chart_metrics(project_id=projectId, user_id=context.user_id, metric_id=metric_id, - data=data) + data=data) if data is None: return {"errors": ["custom metric not found"]} return {"data": data} From f93baad22f2b4b2f28b481f6fd6faaf5526c0cff Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Apr 2022 15:38:06 +0200 Subject: [PATCH 245/294] fix(ui) - call try for new metrics without id --- frontend/app/services/MetricService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/services/MetricService.ts b/frontend/app/services/MetricService.ts index 0007bc8d1..a64cf7318 100644 --- a/frontend/app/services/MetricService.ts +++ b/frontend/app/services/MetricService.ts @@ -97,7 +97,7 @@ export default class MetricService implements IMetricService { * @returns */ fetchSessions(metricId: string, filter: any): Promise { - return this.client.post(`/metrics/${metricId}/sessions`, filter) + return this.client.post(metricId ? `/metrics/${metricId}/sessions` : '/metrics/try/sessions', filter) .then(response => response.json()) .then(response => response.data || []); } From a891c681f637ae91e9462a24592a20a22e4aae1a Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 20 Apr 2022 16:10:00 +0200 Subject: [PATCH 246/294] feat(api): telemetry changes --- api/chalicelib/core/telemetry.py | 2 +- ee/api/chalicelib/core/telemetry.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/chalicelib/core/telemetry.py b/api/chalicelib/core/telemetry.py index 906e9d25e..fa27fbe1c 100644 --- a/api/chalicelib/core/telemetry.py +++ b/api/chalicelib/core/telemetry.py @@ -27,7 +27,7 @@ def compute(): t_projects=COALESCE((SELECT COUNT(*) FROM public.projects WHERE deleted_at ISNULL), 0), t_sessions=COALESCE((SELECT COUNT(*) FROM public.sessions), 0), t_users=COALESCE((SELECT COUNT(*) FROM public.users WHERE deleted_at ISNULL), 0) - RETURNING t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, + RETURNING name,t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, (SELECT openreplay_version()) AS version_number,(SELECT email FROM public.users WHERE role = 'owner' LIMIT 1);""" ) data = cur.fetchone() diff --git a/ee/api/chalicelib/core/telemetry.py b/ee/api/chalicelib/core/telemetry.py index cc71f53d5..9c82290fb 100644 --- a/ee/api/chalicelib/core/telemetry.py +++ b/ee/api/chalicelib/core/telemetry.py @@ -50,7 +50,7 @@ def compute(): FROM public.tenants ) AS all_tenants WHERE tenants.tenant_id = all_tenants.tenant_id - RETURNING t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, + RETURNING name,t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, (SELECT openreplay_version()) AS version_number, (SELECT email FROM public.users WHERE role = 'owner' AND users.tenant_id=tenants.tenant_id LIMIT 1);""" ) From bf509a374d2d056a991aa03a8988dac1cff2bbef Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 20 Apr 2022 16:10:00 +0200 Subject: [PATCH 247/294] feat(api): telemetry changes --- api/chalicelib/core/telemetry.py | 2 +- ee/api/chalicelib/core/telemetry.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/chalicelib/core/telemetry.py b/api/chalicelib/core/telemetry.py index 906e9d25e..fa27fbe1c 100644 --- a/api/chalicelib/core/telemetry.py +++ b/api/chalicelib/core/telemetry.py @@ -27,7 +27,7 @@ def compute(): t_projects=COALESCE((SELECT COUNT(*) FROM public.projects WHERE deleted_at ISNULL), 0), t_sessions=COALESCE((SELECT COUNT(*) FROM public.sessions), 0), t_users=COALESCE((SELECT COUNT(*) FROM public.users WHERE deleted_at ISNULL), 0) - RETURNING t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, + RETURNING name,t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, (SELECT openreplay_version()) AS version_number,(SELECT email FROM public.users WHERE role = 'owner' LIMIT 1);""" ) data = cur.fetchone() diff --git a/ee/api/chalicelib/core/telemetry.py b/ee/api/chalicelib/core/telemetry.py index cc71f53d5..9c82290fb 100644 --- a/ee/api/chalicelib/core/telemetry.py +++ b/ee/api/chalicelib/core/telemetry.py @@ -50,7 +50,7 @@ def compute(): FROM public.tenants ) AS all_tenants WHERE tenants.tenant_id = all_tenants.tenant_id - RETURNING t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, + RETURNING name,t_integrations,t_projects,t_sessions,t_users,user_id,opt_out, (SELECT openreplay_version()) AS version_number, (SELECT email FROM public.users WHERE role = 'owner' AND users.tenant_id=tenants.tenant_id LIMIT 1);""" ) From ee97b9b086b443ed7dbf311f1a1e700919942692 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Wed, 20 Apr 2022 18:28:59 +0200 Subject: [PATCH 248/294] feat(api): custom metrics changed try sessions payload schema --- api/chalicelib/core/custom_metrics.py | 2 +- api/routers/subs/metrics.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 9c3f4bffb..236533b99 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -105,7 +105,7 @@ def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessi return results -def try_sessions(project_id, user_id, data: schemas.TryCustomMetricsPayloadSchema): +def try_sessions(project_id, user_id, data: schemas.CustomMetricSessionsPayloadSchema): results = [] for s in data.series: s.filter.startDate = data.startTimestamp diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 86e9d0064..a33b75d0b 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -108,7 +108,7 @@ def try_custom_metric(projectId: int, data: schemas.TryCustomMetricsPayloadSchem @app.post('/{projectId}/metrics/try/sessions', tags=["dashboard"]) @app.post('/{projectId}/custom_metrics/try/sessions', tags=["customMetrics"]) def try_custom_metric_sessions(projectId: int, - data: schemas.TryCustomMetricsPayloadSchema = Body(...), + data: schemas.CustomMetricSessionsPayloadSchema = Body(...), context: schemas.CurrentContext = Depends(OR_context)): data = custom_metrics.try_sessions(project_id=projectId, user_id=context.user_id, data=data) return {"data": data} From c8015a83ee4cdfe0e4cefd02f869fe0d0b3442c3 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 20 Apr 2022 19:25:54 +0200 Subject: [PATCH 249/294] feat(ui) -widget drilldown payload change --- .../Dashboard/components/WidgetSessions/WidgetSessions.tsx | 4 ++-- frontend/app/mstore/types/widget.ts | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index 9a514dd07..d82cde20b 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -33,12 +33,12 @@ function WidgetSessions(props: Props) { const filteredSessions = getListSessionsBySeries(data, activeSeries); const { dashboardStore, metricStore } = useStore(); const filter = useObserver(() => dashboardStore.drillDownFilter); - const widget: any = metricStore.instance; + const widget: any = useObserver(() => metricStore.instance); const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); useEffect(() => { - widget.fetchSessions({ ...filter, filter: widget.toJsonDrilldown()}).then(res => { + widget.fetchSessions({ ...filter, series: widget.toJsonDrilldown() }).then(res => { setData(res); }); }, [filter.startTimestamp, filter.endTimestamp, filter.filters]); diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index e67fdf4ed..9a6f90fb7 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -158,9 +158,7 @@ export default class Widget implements IWidget { } toJsonDrilldown() { - return { - series: this.series.map((series: any) => series.toJson()), - } + return this.series.map((series: any) => series.toJson()) } toJson() { From 5f22aed09ae62ef32689cf749f406a5605e81af6 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 21 Apr 2022 02:37:15 +0200 Subject: [PATCH 250/294] feat(api): EE metrics support Null and NaN avg result --- ee/api/chalicelib/core/metrics.py | 128 +++++++++++++++--------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/ee/api/chalicelib/core/metrics.py b/ee/api/chalicelib/core/metrics.py index a39e7ed69..65889e28d 100644 --- a/ee/api/chalicelib/core/metrics.py +++ b/ee/api/chalicelib/core/metrics.py @@ -344,8 +344,8 @@ def __get_page_metrics(ch, project_id, startTimestamp, endTimestamp, **args): ch_sub_query += meta_condition ch_sub_query.append("(pages.dom_content_loaded_event_end>0 OR pages.first_contentful_paint>0)") # 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.first_contentful_paint,0)),0) AS avg_first_contentful_pixel + ch_query = f"""SELECT COALESCE(avgOrNull(NULLIF(pages.dom_content_loaded_event_end ,0)),0) AS avg_dom_content_load_start, + COALESCE(avgOrNull(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, @@ -375,7 +375,7 @@ def __get_application_activity(ch, project_id, startTimestamp, endTimestamp, **a meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition - ch_query = f"""SELECT AVG(pages.load_event_end) AS avg_page_load_time + ch_query = f"""SELECT COALESCE(avgOrNull(pages.load_event_end),0) AS avg_page_load_time FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} AND pages.load_event_end>0;""" params = {"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -387,7 +387,7 @@ def __get_application_activity(ch, project_id, startTimestamp, endTimestamp, **a meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition ch_sub_query.append("resources.type= %(type)s") - ch_query = f"""SELECT AVG(resources.duration) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(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)} AND resources.duration>0;""" row = ch.execute(query=ch_query, @@ -433,8 +433,8 @@ def __get_user_activity(ch, project_id, startTimestamp, endTimestamp, **args): meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition ch_sub_query.append("(sessions.pages_count>0 OR sessions.duration>0)") - 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 + ch_query = f"""SELECT COALESCE(CEIL(avgOrNull(NULLIF(sessions.pages_count,0))),0) AS avg_visited_pages, + COALESCE(avgOrNull(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, @@ -460,7 +460,7 @@ 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(resources.duration) AS avg, + COALESCE(avgOrNull(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)} AND resources.duration>0 @@ -477,7 +477,7 @@ def get_slowest_images(project_id, startTimestamp=TimeUTC.now(delta_days=-1), charts = {} ch_query = f"""SELECT url, toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(resources.duration) AS avg + COALESCE(avgOrNull(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.duration>0 GROUP BY url, timestamp @@ -540,7 +540,7 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi "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(resources.duration) AS avg + COALESCE(avgOrNull(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' AND resources.duration>0 @@ -553,7 +553,7 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi 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(resources.duration) AS avg + COALESCE(avgOrNull(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' AND resources.duration>0 @@ -571,7 +571,7 @@ def get_performance(project_id, startTimestamp=TimeUTC.now(delta_days=-1), endTi ch_sub_query_chart += meta_condition ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(pages.load_event_end) AS avg + COALESCE(avgOrNull(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)} AND pages.load_event_end>0 {(f' AND ({" OR ".join(location_constraints)})') if len(location_constraints) > 0 else ""} @@ -896,7 +896,7 @@ def get_resources_loading_time(project_id, startTimestamp=TimeUTC.now(delta_days with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - AVG(resources.duration) AS avg + COALESCE(avgOrNull(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 @@ -906,7 +906,7 @@ def get_resources_loading_time(project_id, startTimestamp=TimeUTC.now(delta_days "endTimestamp": endTimestamp, "value": url, "type": type, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(resources.duration) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(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)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 @@ -929,7 +929,7 @@ def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=- 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 value + COALESCE(avgOrNull(pages.dom_building_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_chart)} GROUP BY timestamp @@ -939,7 +939,7 @@ def get_pages_dom_build_time(project_id, startTimestamp=TimeUTC.now(delta_days=- "endTimestamp": endTimestamp, "value": url, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(pages.dom_building_time) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(pages.dom_building_time),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)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 @@ -970,7 +970,7 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with ch_client.ClickHouseClient() as ch: 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 + COALESCE(avgOrNull(NULLIF(resources.duration,0)),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 @@ -987,7 +987,7 @@ def get_slowest_resources(project_id, startTimestamp=TimeUTC.now(delta_days=-1), 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 + COALESCE(avgOrNull(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 ({" OR ".join([f"endsWith(resources.url_hostpath, %(name_{i})s)>0" for i in range(len(names.keys()))])}) @@ -1044,7 +1044,7 @@ def get_speed_index_location(project_id, startTimestamp=TimeUTC.now(delta_days=- ch_sub_query += meta_condition with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT pages.user_country, AVG(pages.speed_index) AS avg + ch_query = f"""SELECT pages.user_country, COALESCE(avgOrNull(pages.speed_index),0) AS avg FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)} GROUP BY pages.user_country @@ -1053,7 +1053,7 @@ def get_speed_index_location(project_id, startTimestamp=TimeUTC.now(delta_days=- "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(pages.speed_index) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(pages.speed_index),0) AS avg FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 @@ -1073,7 +1073,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 value + COALESCE(avgOrNull(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_chart)} GROUP BY timestamp @@ -1084,7 +1084,7 @@ def get_pages_response_time(project_id, startTimestamp=TimeUTC.now(delta_days=-1 "endTimestamp": endTimestamp, "value": url, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(pages.response_time) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(pages.response_time),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)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 @@ -1114,7 +1114,7 @@ def get_pages_response_time_distribution(project_id, startTimestamp=TimeUTC.now( params={"project_id": project_id, "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)}) - ch_query = f"""SELECT AVG(pages.response_time) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(pages.response_time),0) AS avg FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)};""" avg = ch.execute(query=ch_query, @@ -1247,12 +1247,12 @@ def get_top_metrics(project_id, startTimestamp=TimeUTC.now(delta_days=-1), if value is not None: ch_sub_query.append("pages.url_path = %(value)s") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT (SELECT COALESCE(AVG(pages.response_time),0) 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) AS avg_response_time, + ch_query = f"""SELECT (SELECT COALESCE(avgOrNull(pages.response_time),0) 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) AS avg_response_time, (SELECT COUNT(pages.session_id) FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(ch_sub_query)}) AS count_requests, - (SELECT COALESCE(AVG(pages.first_paint),0) 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) AS avg_first_paint, - (SELECT COALESCE(AVG(pages.dom_content_loaded_event_time),0) 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) AS avg_dom_content_loaded, - (SELECT COALESCE(AVG(pages.ttfb),0) 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) AS avg_till_first_bit, - (SELECT COALESCE(AVG(pages.time_to_interactive),0) 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) AS avg_time_to_interactive;""" + (SELECT COALESCE(avgOrNull(pages.first_paint),0) 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) AS avg_first_paint, + (SELECT COALESCE(avgOrNull(pages.dom_content_loaded_event_time),0) 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) AS avg_dom_content_loaded, + (SELECT COALESCE(avgOrNull(pages.ttfb),0) 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) AS avg_till_first_bit, + (SELECT COALESCE(avgOrNull(pages.time_to_interactive),0) 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) AS avg_time_to_interactive;""" rows = ch.execute(query=ch_query, params={"project_id": project_id, "startTimestamp": startTimestamp, @@ -1274,7 +1274,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 value + COALESCE(avgOrNull(pages.visually_complete),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)} GROUP BY timestamp @@ -1284,7 +1284,7 @@ def get_time_to_render(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, "value": url, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(pages.visually_complete) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(pages.visually_complete),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)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 @@ -1311,7 +1311,7 @@ def get_impacted_sessions_by_slow_pages(project_id, startTimestamp=TimeUTC.now(d 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) + AND (pages.response_time)>(SELECT COALESCE(avgOrNull(pages.response_time),0) FROM pages {"INNER JOIN sessions_metadata USING(session_id)" if len(meta_condition) > 0 else ""} WHERE {" AND ".join(sch_sub_query)})*2 GROUP BY timestamp @@ -1337,7 +1337,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 value + COALESCE(avgOrNull(performance.avg_used_js_heap_size),0) 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 @@ -1347,7 +1347,7 @@ def get_memory_consumption(project_id, startTimestamp=TimeUTC.now(delta_days=-1) "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(performance.avg_used_js_heap_size) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(performance.avg_used_js_heap_size),0) AS avg 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 @@ -1369,7 +1369,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 value + COALESCE(avgOrNull(performance.avg_cpu),0) 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 @@ -1379,7 +1379,7 @@ def get_avg_cpu(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(performance.avg_cpu) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(performance.avg_cpu),0) AS avg 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 @@ -1401,7 +1401,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 value + COALESCE(avgOrNull(performance.avg_fps),0) 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 @@ -1411,7 +1411,7 @@ def get_avg_fps(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(performance.avg_fps) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(performance.avg_fps),0) AS avg 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 @@ -1657,7 +1657,7 @@ def get_slowest_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1), with ch_client.ClickHouseClient() as ch: ch_query = f"""SELECT resources.url_host AS domain, - AVG(resources.duration) AS avg + COALESCE(avgOrNull(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 resources.url_host @@ -1667,7 +1667,7 @@ def get_slowest_domains(project_id, startTimestamp=TimeUTC.now(delta_days=-1), "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, **__get_constraint_values(args)} rows = ch.execute(query=ch_query, params=params) - ch_query = f"""SELECT AVG(resources.duration) AS avg + ch_query = f"""SELECT COALESCE(avgOrNull(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)};""" avg = ch.execute(query=ch_query, params=params)[0]["avg"] if len(rows) > 0 else 0 @@ -1900,7 +1900,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 + COALESCE(avgOrNull(pages.response_end),0) 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 @@ -1963,7 +1963,7 @@ def get_resources_vs_visually_complete(project_id, startTimestamp=TimeUTC.now(de 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, + COALESCE(avgOrNull(NULLIF(s.count,0)),0) AS avg, groupArray([toString(t.type), toString(t.xavg)]) AS types FROM ( SELECT resources.session_id, @@ -1976,7 +1976,7 @@ def get_resources_vs_visually_complete(project_id, startTimestamp=TimeUTC.now(de INNER JOIN (SELECT session_id, type, - AVG(NULLIF(count,0)) AS xavg + COALESCE(avgOrNull(NULLIF(count,0)),0) AS xavg FROM (SELECT resources.session_id, resources.type, 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)} @@ -2111,7 +2111,7 @@ def __get_application_activity_avg_page_load_time(ch, project_id, startTimestamp meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition ch_sub_query.append("pages.load_event_end>0") - ch_query = f"""SELECT AVG(pages.load_event_end) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(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, @@ -2148,7 +2148,7 @@ def get_performance_avg_page_load_time(ch, project_id, startTimestamp=TimeUTC.no ch_sub_query_chart.append("pages.load_event_end>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(pages.load_event_end),0) AS value + COALESCE(avgOrNull(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 ""} @@ -2190,7 +2190,7 @@ def __get_application_activity_avg_image_load_time(ch, project_id, startTimestam ch_sub_query.append("resources.type= %(type)s") ch_sub_query.append("resources.duration>0") ch_query = f"""\ - SELECT COALESCE(AVG(resources.duration),0) AS value + SELECT COALESCE(avgOrNull(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, @@ -2224,7 +2224,7 @@ def get_performance_avg_image_load_time(ch, project_id, startTimestamp=TimeUTC.n "endTimestamp": endTimestamp} ch_sub_query_chart.append("resources.duration>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(resources.duration),0) AS value + COALESCE(avgOrNull(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' @@ -2265,7 +2265,7 @@ def __get_application_activity_avg_request_load_time(ch, project_id, startTimest ch_sub_query += meta_condition ch_sub_query.append("resources.type= %(type)s") ch_sub_query.append("resources.duration>0") - ch_query = f"""SELECT COALESCE(AVG(resources.duration),0) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(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, @@ -2298,7 +2298,7 @@ def get_performance_avg_request_load_time(ch, project_id, startTimestamp=TimeUTC "endTimestamp": endTimestamp} ch_sub_query_chart.append("resources.duration>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(resources.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(resources.duration),0) AS value + COALESCE(avgOrNull(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' @@ -2343,7 +2343,7 @@ def __get_page_metrics_avg_dom_content_load_start(ch, project_id, startTimestamp meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition ch_sub_query.append("pages.dom_content_loaded_event_end>0") - ch_query = f"""SELECT COALESCE(AVG(pages.dom_content_loaded_event_end),0) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(pages.dom_content_loaded_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, "type": 'fetch', "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -2363,7 +2363,7 @@ def __get_page_metrics_avg_dom_content_load_start_chart(ch, project_id, startTim "endTimestamp": endTimestamp} ch_sub_query_chart.append("pages.dom_content_loaded_event_end>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(pages.dom_content_loaded_event_end),0) AS value + COALESCE(avgOrNull(pages.dom_content_loaded_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)} GROUP BY timestamp @@ -2406,7 +2406,7 @@ def __get_page_metrics_avg_first_contentful_pixel(ch, project_id, startTimestamp ch_sub_query.append("pages.first_contentful_paint>0") # changed dom_content_loaded_event_start to dom_content_loaded_event_end ch_query = f"""\ - SELECT COALESCE(AVG(pages.first_contentful_paint),0) AS value + SELECT COALESCE(avgOrNull(pages.first_contentful_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)};""" params = {"project_id": project_id, "type": 'fetch', "startTimestamp": startTimestamp, "endTimestamp": endTimestamp, @@ -2426,7 +2426,7 @@ def __get_page_metrics_avg_first_contentful_pixel_chart(ch, project_id, startTim "endTimestamp": endTimestamp} ch_sub_query_chart.append("pages.first_contentful_paint>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(pages.first_contentful_paint),0) AS value + COALESCE(avgOrNull(pages.first_contentful_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_chart)} GROUP BY timestamp @@ -2469,7 +2469,7 @@ def __get_user_activity_avg_visited_pages(ch, project_id, startTimestamp, endTim meta_condition = __get_meta_constraint(args) ch_sub_query += meta_condition ch_sub_query.append("sessions.pages_count>0") - ch_query = f"""SELECT COALESCE(CEIL(AVG(sessions.pages_count)),0) AS value + ch_query = f"""SELECT COALESCE(CEIL(avgOrNull(sessions.pages_count)),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, @@ -2490,7 +2490,7 @@ def __get_user_activity_avg_visited_pages_chart(ch, project_id, startTimestamp, "endTimestamp": endTimestamp} ch_sub_query_chart.append("sessions.pages_count>0") ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(sessions.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(sessions.pages_count),0) AS value + COALESCE(avgOrNull(sessions.pages_count),0) 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 @@ -2534,7 +2534,7 @@ def __get_user_activity_avg_session_duration(ch, project_id, startTimestamp, end ch_sub_query.append("isNotNull(sessions.duration)") ch_sub_query.append("sessions.duration>0") - ch_query = f"""SELECT COALESCE(AVG(sessions.duration),0) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(sessions.duration),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, @@ -2556,7 +2556,7 @@ def __get_user_activity_avg_session_duration_chart(ch, project_id, startTimestam "endTimestamp": endTimestamp} ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(sessions.datetime, INTERVAL %(step_size)s second ))*1000 AS timestamp, - COALESCE(AVG(sessions.duration),0) AS value + COALESCE(avgOrNull(sessions.duration),0) 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 @@ -2582,7 +2582,7 @@ def get_top_metrics_avg_response_time(project_id, startTimestamp=TimeUTC.now(del ch_sub_query.append("pages.url_path = %(value)s") ch_sub_query_chart.append("pages.url_path = %(value)s") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT COALESCE(AVG(pages.response_time),0) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(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;""" params = {"step_size": step_size, "project_id": project_id, @@ -2657,7 +2657,7 @@ def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta ch_sub_query.append("pages.url_path = %(value)s") ch_sub_query_chart.append("pages.url_path = %(value)s") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT COALESCE(AVG(pages.first_paint),0) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(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;""" params = {"step_size": step_size, "project_id": project_id, @@ -2667,7 +2667,7 @@ def get_top_metrics_avg_first_paint(project_id, startTimestamp=TimeUTC.now(delta rows = ch.execute(query=ch_query, params=params) results = rows[0] ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COALESCE(AVG(pages.first_paint),0) AS value + COALESCE(avgOrNull(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_chart)} AND isNotNull(pages.first_paint) AND pages.first_paint>0 GROUP BY timestamp @@ -2700,7 +2700,7 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no ch_sub_query_chart.append("isNotNull(pages.dom_content_loaded_event_time)") ch_sub_query_chart.append("pages.dom_content_loaded_event_time>0") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT COALESCE(AVG(pages.dom_content_loaded_event_time),0) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(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)};""" params = {"step_size": step_size, "project_id": project_id, @@ -2710,7 +2710,7 @@ def get_top_metrics_avg_dom_content_loaded(project_id, startTimestamp=TimeUTC.no rows = ch.execute(query=ch_query, params=params) results = helper.dict_to_camel_case(rows[0]) ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COALESCE(AVG(pages.dom_content_loaded_event_time),0) AS value + COALESCE(avgOrNull(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_chart)} GROUP BY timestamp @@ -2742,7 +2742,7 @@ def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(de ch_sub_query_chart.append("isNotNull(pages.ttfb)") ch_sub_query_chart.append("pages.ttfb>0") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT COALESCE(AVG(pages.ttfb),0) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(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)};""" params = {"step_size": step_size, "project_id": project_id, @@ -2752,7 +2752,7 @@ def get_top_metrics_avg_till_first_bit(project_id, startTimestamp=TimeUTC.now(de rows = ch.execute(query=ch_query, params=params) results = rows[0] ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COALESCE(AVG(pages.ttfb),0) AS value + COALESCE(avgOrNull(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_chart)} GROUP BY timestamp @@ -2784,7 +2784,7 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n ch_sub_query_chart.append("isNotNull(pages.time_to_interactive)") ch_sub_query_chart.append("pages.time_to_interactive >0") with ch_client.ClickHouseClient() as ch: - ch_query = f"""SELECT COALESCE(AVG(pages.time_to_interactive),0) AS value + ch_query = f"""SELECT COALESCE(avgOrNull(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)};""" params = {"step_size": step_size, "project_id": project_id, @@ -2794,7 +2794,7 @@ def get_top_metrics_avg_time_to_interactive(project_id, startTimestamp=TimeUTC.n rows = ch.execute(query=ch_query, params=params) results = rows[0] ch_query = f"""SELECT toUnixTimestamp(toStartOfInterval(pages.datetime, INTERVAL %(step_size)s second)) * 1000 AS timestamp, - COALESCE(AVG(pages.time_to_interactive),0) AS value + COALESCE(avgOrNull(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_chart)} GROUP BY timestamp From 31efd3002f74763423f30a474d017fe8fb7daf3d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 21 Apr 2022 13:05:49 +0200 Subject: [PATCH 251/294] feat(api): custom metrics handle wrong try-sessions payload --- api/chalicelib/core/custom_metrics.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/chalicelib/core/custom_metrics.py b/api/chalicelib/core/custom_metrics.py index 236533b99..ab0b7d15f 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -107,6 +107,8 @@ def get_sessions(project_id, user_id, metric_id, data: schemas.CustomMetricSessi def try_sessions(project_id, user_id, data: schemas.CustomMetricSessionsPayloadSchema): results = [] + if data.series is None: + return results for s in data.series: s.filter.startDate = data.startTimestamp s.filter.endDate = data.endTimestamp From 3c0d8f81fc2e6757160cd4912f8856823887c58c Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 21 Apr 2022 13:28:54 +0200 Subject: [PATCH 252/294] fix(ui) - data reload on change --- .../components/FilterSeries/FilterSeries.tsx | 7 ++-- .../components/WidgetChart/WidgetChart.tsx | 36 ++++++++++++------- .../components/WidgetForm/WidgetForm.tsx | 20 ++++++----- .../WidgetSessions/WidgetSessions.tsx | 21 ++++++++--- .../components/WidgetView/WidgetView.tsx | 3 +- .../WidgetWrapper/WidgetWrapper.tsx | 3 +- frontend/app/mstore/metricStore.ts | 16 ++++----- frontend/app/mstore/types/filter.ts | 4 +-- frontend/app/mstore/types/widget.ts | 10 +++--- 9 files changed, 68 insertions(+), 52 deletions(-) diff --git a/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx index 337aa1a8a..181a3c8ee 100644 --- a/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx +++ b/frontend/app/components/Dashboard/components/FilterSeries/FilterSeries.tsx @@ -13,8 +13,7 @@ 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'; +import { observer } from 'mobx-react-lite'; interface Props { seriesIndex: number; @@ -37,7 +36,7 @@ function FilterSeries(props: Props) { const [expanded, setExpanded] = useState(true) const { series, seriesIndex } = props; - useEffect(observeChanges, [series]) + useEffect(observeChanges, [series.filter]); const onAddFilter = (filter) => { series.filter.addFilter(filter) @@ -49,12 +48,10 @@ function FilterSeries(props: Props) { 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 ( diff --git a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx index 55552a78f..eb63d8f08 100644 --- a/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx +++ b/frontend/app/components/Dashboard/components/WidgetChart/WidgetChart.tsx @@ -4,29 +4,32 @@ import CustomMetricPercentage from 'App/components/Dashboard/Widgets/CustomMetri 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 { useObserver } from 'mobx-react-lite'; +import { observer, useObserver } from 'mobx-react-lite'; import { Loader } from 'UI'; import { useStore } from 'App/mstore'; import WidgetPredefinedChart from '../WidgetPredefinedChart'; import CustomMetricOverviewChart from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetricOverviewChart'; import { getStartAndEndTimestampsByDensity } from 'Types/dashboard/helper'; +import { debounce } from 'App/utils'; interface Props { metric: any; isWidget?: boolean } function WidgetChart(props: Props) { const { isWidget = false, metric } = props; - const { dashboardStore } = useStore(); + const { dashboardStore, metricStore } = useStore(); + const _metric: any = useObserver(() => metricStore.instance); const period = useObserver(() => dashboardStore.period); const drillDownFilter = useObserver(() => dashboardStore.drillDownFilter); const colors = Styles.customMetricColors; - const [loading, setLoading] = useState(false) + const [loading, setLoading] = useState(true) const isOverviewWidget = metric.metricType === 'predefined' && metric.viewType === 'overview'; const params = { density: isOverviewWidget ? 7 : 70 } const metricParams = { ...params } const prevMetricRef = useRef(); const [data, setData] = useState(metric.data); + const isTableWidget = metric.metricType === 'table' && metric.viewType === 'table'; const isPieChart = metric.metricType === 'table' && metric.viewType === 'pieChart'; @@ -52,21 +55,28 @@ function WidgetChart(props: Props) { } } + const depsString = JSON.stringify(_metric.series); + + + const fetchMetricChartData = (metric, payload, isWidget) => { + setLoading(true) + dashboardStore.fetchMetricChartData(metric, payload, isWidget).then((res: any) => { + setData(res); + }).finally(() => { + setLoading(false); + }); + } + + const debounceRequest: any = React.useCallback(debounce(fetchMetricChartData, 500), []); useEffect(() => { if (prevMetricRef.current && prevMetricRef.current.name !== metric.name) { prevMetricRef.current = metric; return }; prevMetricRef.current = metric; - - setLoading(true); const payload = isWidget ? { ...params } : { ...metricParams, ...metric.toJson() }; - dashboardStore.fetchMetricChartData(metric, payload, isWidget).then((res: any) => { - setData(res); - }).finally(() => { - setLoading(false); - }); - }, [period]); + debounceRequest(metric, payload, isWidget); + }, [period, depsString]); const renderChart = () => { const { metricType, viewType } = metric; @@ -121,10 +131,10 @@ function WidgetChart(props: Props) { return
Unknown
; } return useObserver(() => ( - + {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 13507960d..72c9c33c7 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -4,9 +4,8 @@ import { metricTypes, metricOf, issueOptions } from 'App/constants/filterOptions import { FilterKey } from 'Types/filter/filterType'; import { useStore } from 'App/mstore'; import { useObserver } from 'mobx-react-lite'; -import { HelpText, Button, Icon } from 'UI' +import { 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' import DashboardSelectionModal from '../DashboardSelectionModal/DashboardSelectionModal'; @@ -28,31 +27,34 @@ function WidgetForm(props: Props) { 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 canAddToDashboard = metric.exists() && dashboards.length > 0; const write = ({ target: { value, name } }) => metricStore.merge({ [ name ]: value }); const writeOption = (e, { value, name }) => { - metricStore.merge({ [ name ]: value }); + const obj = { [ name ]: value }; if (name === 'metricValue') { - metricStore.merge({ metricValue: [value] }); + obj['metricValue'] = [value]; } if (name === 'metricOf') { if (value === FilterKey.ISSUE) { - metricStore.merge({ metricValue: ['all'] }); + obj['metricValue'] = ['all']; } } if (name === 'metricType') { if (value === 'timeseries') { - metricStore.merge({ metricOf: timeseriesOptions[0].value, viewType: 'lineChart' }); + obj['metricOf'] = timeseriesOptions[0].value; + obj['viewType'] = 'lineChart'; } else if (value === 'table') { - metricStore.merge({ metricOf: tableOptions[0].value, viewType: 'table' }); + obj['metricOf'] = tableOptions[0].value; + obj['viewType'] = 'table'; } } + + metricStore.merge(obj); }; const onSave = () => { @@ -172,7 +174,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} + // observeChanges={onObserveChanges} />
))} diff --git a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx index d82cde20b..52a670229 100644 --- a/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx +++ b/frontend/app/components/Dashboard/components/WidgetSessions/WidgetSessions.tsx @@ -5,12 +5,14 @@ import { useStore } from 'App/mstore'; import SessionItem from 'Shared/SessionItem'; import { observer, useObserver } from 'mobx-react-lite'; import { DateTime } from 'luxon'; +import { debounce } from 'App/utils'; interface Props { className?: string; } function WidgetSessions(props: Props) { const { className = '' } = props; const [data, setData] = useState([]); + const [loading, setLoading] = useState(false); const [seriesOptions, setSeriesOptions] = useState([ { text: 'All', value: 'all' }, ]); @@ -30,18 +32,27 @@ function WidgetSessions(props: Props) { ]); }, [data]); + const fetchSessions = (metricId, filter) => { + setLoading(true) + widget.fetchSessions(metricId, filter).then(res => { + setData(res) + }).finally(() => { + setLoading(false) + }); + } + const filteredSessions = getListSessionsBySeries(data, activeSeries); const { dashboardStore, metricStore } = useStore(); const filter = useObserver(() => dashboardStore.drillDownFilter); const widget: any = useObserver(() => metricStore.instance); const startTime = DateTime.fromMillis(filter.startTimestamp).toFormat('LLL dd, yyyy HH:mm a'); const endTime = DateTime.fromMillis(filter.endTimestamp).toFormat('LLL dd, yyyy HH:mm a'); + const debounceRequest: any = React.useCallback(debounce(fetchSessions, 1000), []); + const depsString = JSON.stringify(widget.series); useEffect(() => { - widget.fetchSessions({ ...filter, series: widget.toJsonDrilldown() }).then(res => { - setData(res); - }); - }, [filter.startTimestamp, filter.endTimestamp, filter.filters]); + debounceRequest(widget.metricId, { ...filter, series: widget.toJsonDrilldown() }); + }, [filter.startTimestamp, filter.endTimestamp, filter.filters, depsString]); return useObserver(() => (
@@ -71,7 +82,7 @@ function WidgetSessions(props: Props) {
- + props.widget); const isPredefined = widget.metricType === 'predefined'; const dashboard = useObserver(() => dashboardStore.selectedDashboard); + const isOverviewWidget = widget.widgetType === 'predefined' && widget.viewType === 'overview'; const [{ opacity, isDragging }, dragRef] = useDrag({ type: 'item', @@ -117,7 +118,7 @@ function WidgetWrapper(props: Props) { )}
- +
diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts index 89cf153a0..4d4d340a3 100644 --- a/frontend/app/mstore/metricStore.ts +++ b/frontend/app/mstore/metricStore.ts @@ -73,13 +73,13 @@ export default class MetricStore implements IMetricStore { paginatedList: computed, }) - reaction( - () => this.metricsSearch, - (metricsSearch) => { // TODO filter the list for View - this.page = 1 - this.paginatedList - } - ) + // reaction( + // () => this.metricsSearch, + // (metricsSearch) => { // TODO filter the list for View + // this.page = 1 + // this.paginatedList + // } + // ) } // State Actions @@ -92,7 +92,7 @@ export default class MetricStore implements IMetricStore { } merge(object: any) { - this.instance = Object.assign(this.instance, object) + Object.assign(this.instance, object) } reset(id: string) { diff --git a/frontend/app/mstore/types/filter.ts b/frontend/app/mstore/types/filter.ts index 900008fa4..c557c5a1f 100644 --- a/frontend/app/mstore/types/filter.ts +++ b/frontend/app/mstore/types/filter.ts @@ -1,6 +1,4 @@ -import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" -import { FilterKey, FilterType } from 'Types/filter/filterType' -import { filtersMap } from 'Types/filter/newFilter' +import { makeAutoObservable, runInAction, observable, action } from "mobx" import FilterItem from "./filterItem" export interface IFilter { diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts index 9a6f90fb7..a2fac1d80 100644 --- a/frontend/app/mstore/types/widget.ts +++ b/frontend/app/mstore/types/widget.ts @@ -4,6 +4,7 @@ import { DateTime } from 'luxon'; import { IFilter } from "./filter"; import { metricService } from "App/services"; import Session, { ISession } from "App/mstore/types/session"; + export interface IWidget { metricId: any widgetId: any @@ -45,7 +46,7 @@ export interface IWidget { exists(): boolean toWidget(): any setData(data: any): void - fetchSessions(filter: any): Promise + fetchSessions(metricId: any, filter: any): Promise } export default class Widget implements IWidget { public static get ID_KEY():string { return "metricId" } @@ -195,18 +196,15 @@ export default class Widget implements IWidget { }) } - fetchSessions(filter: any): Promise { - this.sessionsLoading = true + fetchSessions(metricId: any, filter: any): Promise { return new Promise((resolve, reject) => { - metricService.fetchSessions(this.metricId, filter).then(response => { + metricService.fetchSessions(metricId, filter).then(response => { resolve(response.map(cat => { return { ...cat, sessions: cat.sessions.map(s => new Session().fromJson(s)) } })) - }).finally(() => { - this.sessionsLoading = false }) }) } From 0cca55315e6919957fdfb3d88ff6e80242ec79f4 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 21 Apr 2022 15:00:13 +0200 Subject: [PATCH 253/294] feat(db): changed metrics default col-values --- .../db/init_dbs/postgresql/1.6.0/1.6.0.sql | 2 +- .../db/init_dbs/postgresql/init_schema.sql | 4 +- .../db/init_dbs/postgresql/1.6.0/1.6.0.sql | 285 +++++++++++++++--- .../db/init_dbs/postgresql/init_schema.sql | 4 +- 4 files changed, 240 insertions(+), 55 deletions(-) diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql index 27b76cbb8..7bb8e6b88 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql @@ -24,7 +24,7 @@ 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 NOT NULL DEFAULT timezone('utc'::text, now()), 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, 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 2f2f34b8d..f01ad9001 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -798,9 +798,9 @@ $$ 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 NOT NULL DEFAULT timezone('utc'::text, now()), deleted_at timestamp, - edited_at timestamp, + edited_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), metric_type metric_type NOT NULL DEFAULT 'timeseries', view_type metric_view_type NOT NULL DEFAULT 'lineChart', metric_of text NOT NULL DEFAULT 'sessionCount', diff --git a/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql b/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql index b507cd511..dbfdb6d85 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql @@ -24,13 +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 edited_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), 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 default_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) ), @@ -65,55 +69,236 @@ 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, 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'), - ('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'), +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'), + ('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'), + ('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":4,"row":2,"position":0}', true, true, true, 'pages_response_time_distribution', 'predefined', 'barChart'), + ('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": 4, + "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":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') + ('Missing Resources', 'resources', '{ + "col": 2, + "row": 2, + "position": 0 + }', true, true, true, 'missing_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, diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 158e5b56a..5e0323b9c 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -957,9 +957,9 @@ $$ 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 NOT NULL DEFAULT timezone('utc'::text, now()), deleted_at timestamp, - edited_at timestamp, + edited_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), 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 320279a8185a5356df2309dad499d9239ede002d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 21 Apr 2022 15:53:23 +0200 Subject: [PATCH 254/294] feat(api): changed metrics list order --- 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 ab0b7d15f..3e7fc100a 100644 --- a/api/chalicelib/core/custom_metrics.py +++ b/api/chalicelib/core/custom_metrics.py @@ -253,7 +253,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 metrics.edited_at, metrics.created_at;""", + ORDER BY metrics.edited_at DESC, metrics.created_at DESC;""", {"project_id": project_id, "user_id": user_id} ) ) From d9546e581f7f05e29d9d4910458a8a3aa3893e9a Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Thu, 21 Apr 2022 16:46:11 +0200 Subject: [PATCH 255/294] feat(frontend):annotation toggle api in player --- .../managers/AssistManager.ts | 64 +++++++++++-------- frontend/app/player/singletone.js | 1 + 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/frontend/app/player/MessageDistributor/managers/AssistManager.ts b/frontend/app/player/MessageDistributor/managers/AssistManager.ts index e888e18a4..49b011634 100644 --- a/frontend/app/player/MessageDistributor/managers/AssistManager.ts +++ b/frontend/app/player/MessageDistributor/managers/AssistManager.ts @@ -370,6 +370,42 @@ export default class AssistManager { } } + toggleAnnotation(enable?: boolean) { + if (typeof enable !== "boolean") { + enable = !!this.annot + } + if (enable && !this.annot) { + const annot = this.annot = new AnnotationCanvas() + annot.mount(this.md.overlay) + annot.canvas.addEventListener("mousedown", e => { + if (!this.socket) { return } + const data = this.md.getInternalViewportCoordinates(e) + annot.start([ data.x, data.y ]) + this.socket.emit("startAnnotation", [ data.x, data.y ]) + }) + annot.canvas.addEventListener("mouseleave", () => { + if (!this.socket) { return } + annot.stop() + this.socket.emit("stopAnnotation") + }) + annot.canvas.addEventListener("mouseup", () => { + if (!this.socket) { return } + annot.stop() + this.socket.emit("stopAnnotation") + }) + annot.canvas.addEventListener("mousemove", e => { + if (!this.socket || !annot.isPainting()) { return } + + const data = this.md.getInternalViewportCoordinates(e) + annot.move([ data.x, data.y ]) + this.socket.emit("moveAnnotation", [ data.x, data.y ]) + }) + } else if (!enable && !!this.annot) { + this.annot.remove() + this.annot = null + } + } + private annot: AnnotationCanvas | null = null private _call() { @@ -396,34 +432,6 @@ export default class AssistManager { call.on('stream', stream => { update({ calling: CallingState.OnCall }) this.callArgs && this.callArgs.onStream(stream) - - if (!this.annot) { - const annot = this.annot = new AnnotationCanvas() - annot.mount(this.md.overlay) - annot.canvas.addEventListener("mousedown", e => { - if (!this.socket) { return } - const data = this.md.getInternalViewportCoordinates(e) - annot.start([ data.x, data.y ]) - this.socket.emit("startAnnotation", [ data.x, data.y ]) - }) - annot.canvas.addEventListener("mouseleave", () => { - if (!this.socket) { return } - annot.stop() - this.socket.emit("stopAnnotation") - }) - annot.canvas.addEventListener("mouseup", () => { - if (!this.socket) { return } - annot.stop() - this.socket.emit("stopAnnotation") - }) - annot.canvas.addEventListener("mousemove", e => { - if (!this.socket || !annot.isPainting()) { return } - - const data = this.md.getInternalViewportCoordinates(e) - annot.move([ data.x, data.y ]) - this.socket.emit("moveAnnotation", [ data.x, data.y ]) - }) - } }); //call.peerConnection.addEventListener("track", e => console.log('newtrack',e.track)) diff --git a/frontend/app/player/singletone.js b/frontend/app/player/singletone.js index 177bb0388..60b8cf8ce 100644 --- a/frontend/app/player/singletone.js +++ b/frontend/app/player/singletone.js @@ -72,6 +72,7 @@ export const callPeer = initCheck((...args) => instance.assistManager.call(...ar export const requestReleaseRemoteControl = initCheck((...args) => instance.assistManager.requestReleaseRemoteControl(...args)) export const markTargets = initCheck((...args) => instance.markTargets(...args)) export const activeTarget = initCheck((...args) => instance.activeTarget(...args)) +export const toggleAnnotation = initCheck((...args) => instance.assistManager.toggleAnnotation(...args)) export const Controls = { jump, From 4a35abc40697b3c986b16f8eaf47f4ef72e426ad Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Thu, 21 Apr 2022 16:52:07 +0200 Subject: [PATCH 256/294] feat(frontend):annotating player state --- .../player/MessageDistributor/managers/AssistManager.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/app/player/MessageDistributor/managers/AssistManager.ts b/frontend/app/player/MessageDistributor/managers/AssistManager.ts index 49b011634..892054cc4 100644 --- a/frontend/app/player/MessageDistributor/managers/AssistManager.ts +++ b/frontend/app/player/MessageDistributor/managers/AssistManager.ts @@ -59,12 +59,14 @@ export interface State { calling: CallingState, peerConnectionStatus: ConnectionStatus, remoteControl: RemoteControlStatus, + annotating: boolean, } export const INITIAL_STATE: State = { calling: CallingState.NoCall, peerConnectionStatus: ConnectionStatus.Connecting, remoteControl: RemoteControlStatus.Disabled, + annotating: false, } const MAX_RECONNECTION_COUNT = 4; @@ -371,8 +373,9 @@ export default class AssistManager { } toggleAnnotation(enable?: boolean) { + if (getState().calling !== CallingState.OnCall) { return } if (typeof enable !== "boolean") { - enable = !!this.annot + enable = !!getState().annotating } if (enable && !this.annot) { const annot = this.annot = new AnnotationCanvas() @@ -400,9 +403,11 @@ export default class AssistManager { annot.move([ data.x, data.y ]) this.socket.emit("moveAnnotation", [ data.x, data.y ]) }) + update({ annotating: true }) } else if (!enable && !!this.annot) { this.annot.remove() this.annot = null + update({ annotating: false }) } } From f13790e58b5e75086e7a46cb1112d04387ea6973 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Thu, 21 Apr 2022 17:18:05 +0200 Subject: [PATCH 257/294] fix(tracker-axios):3.5.1:peerDependency axios@0.x --- tracker/tracker-axios/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tracker/tracker-axios/package.json b/tracker/tracker-axios/package.json index abb545c1d..346b8d7bb 100644 --- a/tracker/tracker-axios/package.json +++ b/tracker/tracker-axios/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-axios", "description": "Tracker plugin for axios requests recording", - "version": "3.5.0", + "version": "3.5.1", "keywords": [ "axios", "logging", @@ -21,11 +21,11 @@ "dependencies": {}, "peerDependencies": { "@openreplay/tracker": "^3.4.8", - "axios": "^0.21.2" + "axios": "0.x" }, "devDependencies": { "@openreplay/tracker": "^3.4.9", - "axios": "^0.21.2", + "axios": "^0.26.0", "prettier": "^1.18.2", "replace-in-files-cli": "^1.0.0", "typescript": "^4.6.0-dev.20211126" From 6f547d4c83de4ec9b4d5f6a61efc739f89bcb4cb Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Thu, 21 Apr 2022 17:28:47 +0200 Subject: [PATCH 258/294] fix(backend): fix fetch sql insertion --- backend/pkg/db/postgres/messages-web.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pkg/db/postgres/messages-web.go b/backend/pkg/db/postgres/messages-web.go index abde2a4e0..02bcc284b 100644 --- a/backend/pkg/db/postgres/messages-web.go +++ b/backend/pkg/db/postgres/messages-web.go @@ -230,7 +230,7 @@ func (conn *Conn) InsertWebFetchEvent(sessionID uint64, savePayload bool, e *Fet duration, success ) VALUES ( $1, $2, $3, - $4, $5, $6, $7 + $4, $5, $6, $7, $8, $9, $10::smallint, NULLIF($11, '')::http_method, $12, $13 ) ON CONFLICT DO NOTHING`, From af7bb58825072e40e78585480ba9819f7692c0d7 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Thu, 21 Apr 2022 18:30:45 +0200 Subject: [PATCH 259/294] fix(backend): do not insert pase_path to pages --- backend/pkg/db/postgres/messages-web.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/pkg/db/postgres/messages-web.go b/backend/pkg/db/postgres/messages-web.go index 02bcc284b..e540dc6a0 100644 --- a/backend/pkg/db/postgres/messages-web.go +++ b/backend/pkg/db/postgres/messages-web.go @@ -70,16 +70,14 @@ func (conn *Conn) InsertWebPageEvent(sessionID uint64, e *PageEvent) error { session_id, message_id, timestamp, referrer, base_referrer, host, path, query, dom_content_loaded_time, load_time, response_end, first_paint_time, first_contentful_paint_time, speed_index, visually_complete, time_to_interactive, - response_time, dom_building_time, - base_path + response_time, dom_building_time ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, NULLIF($9, 0), NULLIF($10, 0), NULLIF($11, 0), NULLIF($12, 0), NULLIF($13, 0), NULLIF($14, 0), NULLIF($15, 0), NULLIF($16, 0), - NULLIF($17, 0), NULLIF($18, 0), - '' + NULLIF($17, 0), NULLIF($18, 0) ) `, sessionID, e.MessageID, e.Timestamp, @@ -87,7 +85,7 @@ func (conn *Conn) InsertWebPageEvent(sessionID uint64, e *PageEvent) error { host, path, query, e.DomContentLoadedEventEnd, e.LoadEventEnd, e.ResponseEnd, e.FirstPaint, e.FirstContentfulPaint, e.SpeedIndex, e.VisuallyComplete, e.TimeToInteractive, - calcResponseTime(e), calcDomBuildingTime(e), + calcResponseTime(e), calcDomBuildingTime(e) ); err != nil { return err } From 3cb43a4e3c8004b62b6ef94dcbc474e906cfd0b2 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 21 Apr 2022 18:01:29 +0200 Subject: [PATCH 260/294] fix(ui) - compile errors --- .../Assist/ChatWindow/ChatWindow.tsx | 1 + .../CustomMetriLineChart.tsx | 2 +- .../CustomMetricPieChart.tsx | 2 +- .../ErrorsByOrigin/ErrorsByOrigin.tsx | 3 + .../ResponseTimeDistribution.tsx | 2 +- .../DashboardSideMenu/DashboardSideMenu.tsx | 1 + .../WidgetWrapper/TemplateOverlay.tsx | 1 + .../components/WidgetWrapper/WidgetIcon.tsx | 1 + frontend/app/components/Modal/index.tsx | 1 + .../Player/Overlay/ElementsMarker/Marker.tsx | 1 + .../CustomMetricForm/CustomMetricForm.tsx | 2 +- .../components/ui/Pagination/Pagination.tsx | 1 + frontend/package-lock.json | 2747 +++++++++-------- frontend/package.json | 2 +- frontend/tsconfig.json | 3 +- 15 files changed, 1457 insertions(+), 1313 deletions(-) diff --git a/frontend/app/components/Assist/ChatWindow/ChatWindow.tsx b/frontend/app/components/Assist/ChatWindow/ChatWindow.tsx index 36bc0765b..b57f5ca35 100644 --- a/frontend/app/components/Assist/ChatWindow/ChatWindow.tsx +++ b/frontend/app/components/Assist/ChatWindow/ChatWindow.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import React, { useState, FC, useEffect } from 'react' import VideoContainer from '../components/VideoContainer' import { Icon, Popup, Button } from 'UI' diff --git a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx index 957f44deb..198afb088 100644 --- a/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx +++ b/frontend/app/components/Dashboard/Widgets/CustomMetricsWidgets/CustomMetriLineChart/CustomMetriLineChart.tsx @@ -20,7 +20,7 @@ function CustomMetriLineChart(props: Props) { margin={Styles.chartMargins} // syncId={ showSync ? "domainsErrors_4xx" : undefined } onClick={onClick} - isAnimationActive={ false } + // isAnimationActive={ false } > { const RADIAN = Math.PI / 180; let radius1 = 15 + innerRadius + (outerRadius - innerRadius); diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx index 87fd42eb3..d7aefebd0 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ErrorsByOrigin/ErrorsByOrigin.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import React from 'react'; import { NoContent } from 'UI'; import { Styles } from '../../common'; @@ -39,6 +40,8 @@ function ErrorsByOrigin(props: Props) { 1st Party} dataKey="firstParty" stackId="a" fill={Styles.colors[0]} /> 3rd Party} dataKey="thirdParty" stackId="a" fill={Styles.colors[2]} /> + {/* 1st Party} dataKey="firstParty" stackId="a" fill={Styles.colors[0]} /> + 3rd Party} dataKey="thirdParty" stackId="a" fill={Styles.colors[2]} /> */} diff --git a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/ResponseTimeDistribution.tsx b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/ResponseTimeDistribution.tsx index 4cfb032e2..2f79230e5 100644 --- a/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/ResponseTimeDistribution.tsx +++ b/frontend/app/components/Dashboard/Widgets/PredefinedWidgets/ResponseTimeDistribution/ResponseTimeDistribution.tsx @@ -99,7 +99,7 @@ function ResponseTimeDistribution(props: Props) { label={`${item.percentile}th Percentile (${item.responseTime}ms)`} /> } - allowDecimals={false} + // allowDecimals={false} x={item.responseTime} strokeWidth={0} strokeOpacity={1} diff --git a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx index 72de0ea3d..9dd64733c 100644 --- a/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx +++ b/frontend/app/components/Dashboard/components/DashboardSideMenu/DashboardSideMenu.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import { useObserver } from 'mobx-react-lite'; import React from 'react'; import { SideMenuitem, SideMenuHeader, Icon, Button } from 'UI'; diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx index 4cb9e17cf..d95e9d894 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/TemplateOverlay.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import React from 'react'; import { Tooltip } from 'react-tippy'; diff --git a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetIcon.tsx b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetIcon.tsx index 5ed7c2a45..92d55ae47 100644 --- a/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetIcon.tsx +++ b/frontend/app/components/Dashboard/components/WidgetWrapper/WidgetIcon.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import React from 'react'; import { Icon } from 'UI'; import { Tooltip } from 'react-tippy'; diff --git a/frontend/app/components/Modal/index.tsx b/frontend/app/components/Modal/index.tsx index be4c4638d..a653ed24f 100644 --- a/frontend/app/components/Modal/index.tsx +++ b/frontend/app/components/Modal/index.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import React, { Component, createContext } from 'react'; import Modal from './Modal'; diff --git a/frontend/app/components/Session_/Player/Overlay/ElementsMarker/Marker.tsx b/frontend/app/components/Session_/Player/Overlay/ElementsMarker/Marker.tsx index 92568ff49..8149a9278 100644 --- a/frontend/app/components/Session_/Player/Overlay/ElementsMarker/Marker.tsx +++ b/frontend/app/components/Session_/Player/Overlay/ElementsMarker/Marker.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import React from 'react'; import type { MarkedTarget } from 'Player/MessageDistributor/StatedScreen/StatedScreen'; import { Tooltip } from 'react-tippy'; diff --git a/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx b/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx index 28a0cbc42..89d78ea7a 100644 --- a/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx +++ b/frontend/app/components/shared/CustomMetrics/CustomMetricForm/CustomMetricForm.tsx @@ -98,7 +98,7 @@ function CustomMetricForm(props: Props) { autoFocus={ true } className="text-lg" name="name" - style={{ fontSize: '18px', padding: '10px', fontWeight: '600'}} + style={{ fontSize: '18px', padding: '10px', fontWeight: 600}} value={ metric.name } onChange={ write } placeholder="Metric Title" diff --git a/frontend/app/components/ui/Pagination/Pagination.tsx b/frontend/app/components/ui/Pagination/Pagination.tsx index 0e552ea69..a915feee0 100644 --- a/frontend/app/components/ui/Pagination/Pagination.tsx +++ b/frontend/app/components/ui/Pagination/Pagination.tsx @@ -1,3 +1,4 @@ +//@ts-nocheck import React from 'react' import { Icon } from 'UI' import cn from 'classnames' diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5b5c1de48..8a844720b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -46,7 +46,7 @@ "react-tippy": "^1.4.0", "react-toastify": "^5.5.0", "react-virtualized": "^9.22.2", - "recharts": "^1.8.5", + "recharts": "^2.1.9", "redux": "^4.0.5", "redux-immutable": "^4.0.0", "redux-thunk": "^2.3.0", @@ -183,25 +183,25 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", + "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", + "@babel/generator": "^7.17.9", "@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/helpers": "^7.17.9", + "@babel/parser": "^7.17.9", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", + "@babel/traverse": "^7.17.9", "@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", + "json5": "^2.2.1", "semver": "^6.3.0" }, "engines": { @@ -213,9 +213,9 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", + "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", "dev": true, "dependencies": { "@babel/types": "^7.17.0", @@ -279,15 +279,15 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz", + "integrity": "sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ==", "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-function-name": "^7.17.9", + "@babel/helper-member-expression-to-functions": "^7.17.7", "@babel/helper-optimise-call-expression": "^7.16.7", "@babel/helper-replace-supers": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7" @@ -359,26 +359,13 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", + "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", "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" + "@babel/types": "^7.17.0" }, "engines": { "node": ">=6.9.0" @@ -560,13 +547,13 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", + "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", "dev": true, "dependencies": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", + "@babel/traverse": "^7.17.9", "@babel/types": "^7.17.0" }, "engines": { @@ -574,9 +561,9 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", + "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -588,9 +575,9 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", + "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -682,14 +669,15 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.9.tgz", + "integrity": "sha512-EfH2LZ/vPa2wuPwJ26j+kYRkaubf89UlwxKXtxqEm57HrgSEYDB8t4swFP+p8LcI9yiP9ZRJJjo/58hS6BnaDA==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-create-class-features-plugin": "^7.17.9", "@babel/helper-plugin-utils": "^7.16.7", "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", "@babel/plugin-syntax-decorators": "^7.17.0", "charcodes": "^0.2.0" }, @@ -1435,9 +1423,9 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz", + "integrity": "sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw==", "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.17.7", @@ -1629,12 +1617,12 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz", + "integrity": "sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ==", "dev": true, "dependencies": { - "regenerator-transform": "^0.14.2" + "regenerator-transform": "^0.15.0" }, "engines": { "node": ">=6.9.0" @@ -1960,9 +1948,9 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -1971,9 +1959,9 @@ } }, "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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.9.tgz", + "integrity": "sha512-WxYHHUWF2uZ7Hp1K+D1xQgbgkGUfA+5UPOegEXGt2Y5SMog/rYCVaifLZDbw8UkNXozEqqrZTy6bglL7xTaCOw==", "dev": true, "dependencies": { "core-js-pure": "^3.20.2", @@ -1998,18 +1986,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", + "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", + "@babel/generator": "^7.17.9", "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", + "@babel/parser": "^7.17.9", "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" @@ -2289,9 +2277,9 @@ "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==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -2484,9 +2472,9 @@ } }, "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==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2558,9 +2546,9 @@ } }, "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==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.5.tgz", + "integrity": "sha512-RbG7h6TuP6nFFYKJwbcToA1rjC1FyPg25NR2noAZ0vKI+la01KTSRPkuVPE+U88jXv7javx2JHglUcL1MHcshQ==", "dev": true, "dependencies": { "ansi-html-community": "^0.0.8", @@ -2608,9 +2596,9 @@ } }, "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==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", + "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==", "dev": true, "funding": { "type": "opencollective", @@ -2618,19 +2606,19 @@ } }, "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==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", + "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==" }, "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==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-3.0.1.tgz", + "integrity": "sha512-blqduwV86oiKw2Gr44wbe3pj3Z/OsXirc7ybCv9F/pLAR+Aih8F3rjeJzK0ANgtYKv5lCpkGVoZAeKitKDaD/g==" }, "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==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-3.0.1.tgz", + "integrity": "sha512-XjDVbs3ZU16CO1h5Q3Ew2RPJqmZBDE/EVf1LYp6ePEffs3V/MX9ZbL5bJr8qiK5SbGmUMuDoaFgyKacYz8prRA==" }, "node_modules/@semantic-ui-react/event-stack": { "version": "3.1.2", @@ -2734,18 +2722,18 @@ "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.22.tgz", + "integrity": "sha512-P/R+Jsxh7pawKLYo8MtE3QU/ilRFKbtCewV/T1o5U/gm8v7hKQdFz3YdRMAra4QuCY8bQIp7MKd2HrB5aH5a1A==", "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/api": "6.4.22", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/router": "6.4.19", - "@storybook/theming": "6.4.19", + "@storybook/router": "6.4.22", + "@storybook/theming": "6.4.22", "@types/webpack-env": "^1.16.0", "core-js": "^3.8.2", "global": "^4.4.0", @@ -2761,18 +2749,18 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.4.22.tgz", + "integrity": "sha512-lAVI3o2hKupYHXFTt+1nqFct942up5dHH6YD7SZZJGyW21dwKC3HK1IzCsTawq3fZAKkgWFgmOO649hKk60yKg==", "dev": true, "dependencies": { - "@storybook/channels": "6.4.19", - "@storybook/client-logger": "6.4.19", - "@storybook/core-events": "6.4.19", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/router": "6.4.19", + "@storybook/router": "6.4.22", "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.4.19", + "@storybook/theming": "6.4.22", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", @@ -2794,9 +2782,9 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.4.22.tgz", + "integrity": "sha512-A+GgGtKGnBneRFSFkDarUIgUTI8pYFdLmUVKEAGdh2hL+vLXAz9A46sEY7C8LQ85XWa8TKy3OTDxqR4+4iWj3A==", "dev": true, "dependencies": { "@babel/core": "^7.12.10", @@ -2820,22 +2808,22 @@ "@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/addons": "6.4.22", + "@storybook/api": "6.4.22", + "@storybook/channel-postmessage": "6.4.22", + "@storybook/channels": "6.4.22", + "@storybook/client-api": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/components": "6.4.22", + "@storybook/core-common": "6.4.22", + "@storybook/core-events": "6.4.22", + "@storybook/node-logger": "6.4.22", + "@storybook/preview-web": "6.4.22", + "@storybook/router": "6.4.22", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.19", - "@storybook/theming": "6.4.19", - "@storybook/ui": "6.4.19", + "@storybook/store": "6.4.22", + "@storybook/theming": "6.4.22", + "@storybook/ui": "6.4.22", "@types/node": "^14.0.10", "@types/webpack": "^4.41.26", "autoprefixer": "^9.8.6", @@ -3207,9 +3195,9 @@ } }, "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==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3285,14 +3273,14 @@ "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.4.22.tgz", + "integrity": "sha512-gt+0VZLszt2XZyQMh8E94TqjHZ8ZFXZ+Lv/Mmzl0Yogsc2H+6VzTTQO4sv0IIx6xLbpgG72g5cr8VHsxW5kuDQ==", "dev": true, "dependencies": { - "@storybook/channels": "6.4.19", - "@storybook/client-logger": "6.4.19", - "@storybook/core-events": "6.4.19", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "core-js": "^3.8.2", "global": "^4.4.0", "qs": "^6.10.0", @@ -3304,13 +3292,13 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-6.4.22.tgz", + "integrity": "sha512-Bm/FcZ4Su4SAK5DmhyKKfHkr7HiHBui6PNutmFkASJInrL9wBduBfN8YQYaV7ztr8ezoHqnYRx8sj28jpwa6NA==", "dev": true, "dependencies": { - "@storybook/channels": "6.4.19", - "@storybook/client-logger": "6.4.19", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", "core-js": "^3.8.2", "global": "^4.4.0", "telejson": "^5.3.2" @@ -3321,9 +3309,9 @@ } }, "node_modules/@storybook/channels": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.19.tgz", - "integrity": "sha512-EwyoncFvTfmIlfsy8jTfayCxo2XchPkZk/9txipugWSmc057HdklMKPLOHWP0z5hLH0IbVIKXzdNISABm36jwQ==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.22.tgz", + "integrity": "sha512-cfR74tu7MLah1A8Rru5sak71I+kH2e/sY6gkpVmlvBj4hEmdZp4Puj9PTeaKcMXh9DgIDPNA5mb8yvQH6VcyxQ==", "dev": true, "dependencies": { "core-js": "^3.8.2", @@ -3336,18 +3324,18 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.4.22.tgz", + "integrity": "sha512-sO6HJNtrrdit7dNXQcZMdlmmZG1k6TswH3gAyP/DoYajycrTwSJ6ovkarzkO+0QcJ+etgra4TEdTIXiGHBMe/A==", "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/addons": "6.4.22", + "@storybook/channel-postmessage": "6.4.22", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/store": "6.4.19", + "@storybook/store": "6.4.22", "@types/qs": "^6.9.5", "@types/webpack-env": "^1.16.0", "core-js": "^3.8.2", @@ -3372,9 +3360,9 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.4.22.tgz", + "integrity": "sha512-LXhxh/lcDsdGnK8kimqfhu3C0+D2ylCSPPQNbU0IsLRmTfbpQYMdyl0XBjPdHiRVwlL7Gkw5OMjYemQgJ02zlw==", "dev": true, "dependencies": { "core-js": "^3.8.2", @@ -3386,15 +3374,15 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.4.22.tgz", + "integrity": "sha512-dCbXIJF9orMvH72VtAfCQsYbe57OP7fAADtR6YTwfCw9Sm1jFuZr8JbblQ1HcrXEoJG21nOyad3Hm5EYVb/sBw==", "dev": true, "dependencies": { "@popperjs/core": "^2.6.0", - "@storybook/client-logger": "6.4.19", + "@storybook/client-logger": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/theming": "6.4.19", + "@storybook/theming": "6.4.22", "@types/color-convert": "^2.0.0", "@types/overlayscrollbars": "^1.12.0", "@types/react-syntax-highlighter": "11.0.5", @@ -3426,20 +3414,20 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.4.22.tgz", + "integrity": "sha512-KZYJt7GM5NgKFXbPRZZZPEONZ5u/tE/cRbMdkn/zWN3He8+VP+65/tz8hbriI/6m91AWVWkBKrODSkeq59NgRA==", "dev": true, "dependencies": { - "@storybook/core-client": "6.4.19", - "@storybook/core-server": "6.4.19" + "@storybook/core-client": "6.4.22", + "@storybook/core-server": "6.4.22" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/builder-webpack5": "6.4.19", + "@storybook/builder-webpack5": "6.4.22", "react": "^16.8.0 || ^17.0.0", "react-dom": "^16.8.0 || ^17.0.0", "webpack": "*" @@ -3454,21 +3442,21 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-6.4.22.tgz", + "integrity": "sha512-uHg4yfCBeM6eASSVxStWRVTZrAnb4FT6X6v/xDqr4uXCpCttZLlBzrSDwPBLNNLtCa7ntRicHM8eGKIOD5lMYQ==", "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/addons": "6.4.22", + "@storybook/channel-postmessage": "6.4.22", + "@storybook/channel-websocket": "6.4.22", + "@storybook/client-api": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/preview-web": "6.4.19", - "@storybook/store": "6.4.19", - "@storybook/ui": "6.4.19", + "@storybook/preview-web": "6.4.22", + "@storybook/store": "6.4.22", + "@storybook/ui": "6.4.22", "airbnb-js-shims": "^2.2.1", "ansi-to-html": "^0.6.11", "core-js": "^3.8.2", @@ -3496,9 +3484,9 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-6.4.22.tgz", + "integrity": "sha512-PD3N/FJXPNRHeQS2zdgzYFtqPLdi3MLwAicbnw+U3SokcsspfsAuyYHZOYZgwO8IAEKy6iCc7TpBdiSJZ/vAKQ==", "dev": true, "dependencies": { "@babel/core": "^7.12.10", @@ -3522,7 +3510,7 @@ "@babel/preset-react": "^7.12.10", "@babel/preset-typescript": "^7.12.7", "@babel/register": "^7.12.1", - "@storybook/node-logger": "6.4.19", + "@storybook/node-logger": "6.4.22", "@storybook/semver": "^7.3.2", "@types/node": "^14.0.10", "@types/pretty-hrtime": "^1.0.0", @@ -3659,9 +3647,9 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.4.22.tgz", + "integrity": "sha512-5GYY5+1gd58Gxjqex27RVaX6qbfIQmJxcbzbNpXGNSqwqAuIIepcV1rdCVm6I4C3Yb7/AQ3cN5dVbf33QxRIwA==", "dev": true, "dependencies": { "core-js": "^3.8.2" @@ -3672,22 +3660,22 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.4.22.tgz", + "integrity": "sha512-wFh3e2fa0un1d4+BJP+nd3FVWUO7uHTqv3OGBfOmzQMKp4NU1zaBNdSQG7Hz6mw0fYPBPZgBjPfsJRwIYLLZyw==", "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/builder-webpack4": "6.4.22", + "@storybook/core-client": "6.4.22", + "@storybook/core-common": "6.4.22", + "@storybook/core-events": "6.4.22", "@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/csf-tools": "6.4.22", + "@storybook/manager-webpack4": "6.4.22", + "@storybook/node-logger": "6.4.22", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.19", + "@storybook/store": "6.4.22", "@types/node": "^14.0.10", "@types/node-fetch": "^2.5.7", "@types/pretty-hrtime": "^1.0.0", @@ -3725,8 +3713,8 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@storybook/builder-webpack5": "6.4.19", - "@storybook/manager-webpack5": "6.4.19", + "@storybook/builder-webpack5": "6.4.22", + "@storybook/manager-webpack5": "6.4.22", "react": "^16.8.0 || ^17.0.0", "react-dom": "^16.8.0 || ^17.0.0" }, @@ -3822,9 +3810,9 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-6.4.22.tgz", + "integrity": "sha512-LMu8MZAiQspJAtMBLU2zitsIkqQv7jOwX7ih5JrXlyaDticH7l2j6Q+1mCZNWUOiMTizj0ivulmUsSaYbpToSw==", "dev": true, "dependencies": { "@babel/core": "^7.12.10", @@ -3851,20 +3839,20 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/manager-webpack4/-/manager-webpack4-6.4.22.tgz", + "integrity": "sha512-nzhDMJYg0vXdcG0ctwE6YFZBX71+5NYaTGkxg3xT7gbgnP1YFXn9gVODvgq3tPb3gcRapjyOIxUa20rV+r8edA==", "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", + "@storybook/addons": "6.4.22", + "@storybook/core-client": "6.4.22", + "@storybook/core-common": "6.4.22", + "@storybook/node-logger": "6.4.22", + "@storybook/theming": "6.4.22", + "@storybook/ui": "6.4.22", "@types/node": "^14.0.10", "@types/webpack": "^4.41.26", "babel-loader": "^8.0.0", @@ -4047,9 +4035,9 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.4.22.tgz", + "integrity": "sha512-sUXYFqPxiqM7gGH7gBXvO89YEO42nA4gBicJKZjj9e+W4QQLrftjF9l+mAw2K0mVE10Bn7r4pfs5oEZ0aruyyA==", "dev": true, "dependencies": { "@types/npmlog": "^4.1.2", @@ -4116,17 +4104,17 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/preview-web/-/preview-web-6.4.22.tgz", + "integrity": "sha512-sWS+sgvwSvcNY83hDtWUUL75O2l2LY/GTAS0Zp2dh3WkObhtuJ/UehftzPZlZmmv7PCwhb4Q3+tZDKzMlFxnKQ==", "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/addons": "6.4.22", + "@storybook/channel-postmessage": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/store": "6.4.19", + "@storybook/store": "6.4.22", "ansi-to-html": "^0.6.11", "core-js": "^3.8.2", "global": "^4.4.0", @@ -4148,22 +4136,22 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-6.4.22.tgz", + "integrity": "sha512-5BFxtiguOcePS5Ty/UoH7C6odmvBYIZutfiy4R3Ua6FYmtxac5vP9r5KjCz1IzZKT8mCf4X+PuK1YvDrPPROgQ==", "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/addons": "6.4.22", + "@storybook/core": "6.4.22", + "@storybook/core-common": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/node-logger": "6.4.19", + "@storybook/node-logger": "6.4.22", "@storybook/react-docgen-typescript-plugin": "1.0.2-canary.253f8c1.0", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.19", + "@storybook/store": "6.4.22", "@types/webpack-env": "^1.16.0", "babel-plugin-add-react-displayname": "^0.0.5", "babel-plugin-named-asset-import": "^0.3.1", @@ -4311,12 +4299,12 @@ "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.4.22.tgz", + "integrity": "sha512-zeuE8ZgFhNerQX8sICQYNYL65QEi3okyzw7ynF58Ud6nRw4fMxSOHcj2T+nZCIU5ufozRL4QWD/Rg9P2s/HtLw==", "dev": true, "dependencies": { - "@storybook/client-logger": "6.4.19", + "@storybook/client-logger": "6.4.22", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", @@ -4338,9 +4326,9 @@ } }, "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==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz", + "integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==", "dev": true, "dependencies": { "history": "^5.2.0" @@ -4350,13 +4338,13 @@ } }, "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==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz", + "integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==", "dev": true, "dependencies": { "history": "^5.2.0", - "react-router": "6.2.2" + "react-router": "6.3.0" }, "peerDependencies": { "react": ">=16.8", @@ -4435,14 +4423,14 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/store/-/store-6.4.22.tgz", + "integrity": "sha512-lrmcZtYJLc2emO+1l6AG4Txm9445K6Pyv9cGAuhOJ9Kks0aYe0YtvMkZVVry0RNNAIv6Ypz72zyKc/QK+tZLAQ==", "dev": true, "dependencies": { - "@storybook/addons": "6.4.19", - "@storybook/client-logger": "6.4.19", - "@storybook/core-events": "6.4.19", + "@storybook/addons": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", @@ -4475,15 +4463,15 @@ } }, "node_modules/@storybook/theming": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.19.tgz", - "integrity": "sha512-V4pWmTvAxmbHR6B3jA4hPkaxZPyExHvCToy7b76DpUTpuHihijNDMAn85KhOQYIeL9q14zP/aiz899tOHsOidg==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.22.tgz", + "integrity": "sha512-NVMKH/jxSPtnMTO4VCN1k47uztq+u9fWv4GSnzq/eezxdGg9ceGL4/lCrNGoNajht9xbrsZ4QvsJ/V2sVGM8wA==", "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", + "@storybook/client-logger": "6.4.22", "core-js": "^3.8.2", "deep-object-diff": "^1.1.0", "emotion-theming": "^10.0.27", @@ -4503,21 +4491,21 @@ } }, "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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.22.tgz", + "integrity": "sha512-UVjMoyVsqPr+mkS1L7m30O/xrdIEgZ5SCWsvqhmyMUok3F3tRB+6M+OA5Yy+cIVfvObpA7MhxirUT1elCGXsWQ==", "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/addons": "6.4.22", + "@storybook/api": "6.4.22", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/components": "6.4.22", + "@storybook/core-events": "6.4.22", + "@storybook/router": "6.4.22", "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.4.19", + "@storybook/theming": "6.4.22", "copy-to-clipboard": "^3.3.1", "core-js": "^3.8.2", "core-js-pure": "^3.8.2", @@ -4584,6 +4572,45 @@ "postcss": "7.x.x" } }, + "node_modules/@types/d3-color": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.3.tgz", + "integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w==" + }, + "node_modules/@types/d3-interpolate": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-2.0.2.tgz", + "integrity": "sha512-lElyqlUfIPyWG/cD475vl6msPL4aMU7eJvx1//Q177L8mdXoVPFl1djIESF2FKnc0NyaHvQlJpWwKJYwAhUoCw==", + "dependencies": { + "@types/d3-color": "^2" + } + }, + "node_modules/@types/d3-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.2.tgz", + "integrity": "sha512-3YHpvDw9LzONaJzejXLOwZ3LqwwkoXb9LI2YN7Hbd6pkGo5nIlJ09ul4bQhBN4hQZJKmUpX8HkVqbzgUKY48cg==" + }, + "node_modules/@types/d3-scale": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz", + "integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==", + "dependencies": { + "@types/d3-time": "^2" + } + }, + "node_modules/@types/d3-shape": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.1.3.tgz", + "integrity": "sha512-HAhCel3wP93kh4/rq+7atLdybcESZ5bRHDEZUojClyZWsRuEMo3A52NGYJSh48SxfxEU6RZIVbZL2YFZ2OAlzQ==", + "dependencies": { + "@types/d3-path": "^2" + } + }, + "node_modules/@types/d3-time": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==" + }, "node_modules/@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -4649,10 +4676,10 @@ "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==", - "devOptional": true + "version": "14.18.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.13.tgz", + "integrity": "sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA==", + "dev": true }, "node_modules/@types/node-fetch": { "version": "2.6.1", @@ -4701,10 +4728,10 @@ "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==", - "devOptional": true + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true }, "node_modules/@types/q": { "version": "1.5.5", @@ -4719,10 +4746,10 @@ "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==", - "devOptional": true, + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.5.tgz", + "integrity": "sha512-UPxNGInDCIKlfqBrm8LDXYWNfLHwIdisWcsH5GpMyGjhEDLFgTtlRBaoWuCua9HcyuE0rMkmAeZ3FXV1pYLIYQ==", + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4742,13 +4769,18 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "devOptional": true + "dev": true + }, + "node_modules/@types/resize-observer-browser": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz", + "integrity": "sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==" }, "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==", - "devOptional": true + "dev": true }, "node_modules/@types/source-list-map": { "version": "0.1.2", @@ -4763,9 +4795,9 @@ "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==", + "version": "3.13.2", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.2.tgz", + "integrity": "sha512-/xFrPIo+4zOeNGtVMbf9rUm0N+i4pDf1ynExomqtokIJmVzR3962lJ1UE+MmexMkA0cmN9oTzg5Xcbwge0Ij2Q==", "dev": true, "dependencies": { "source-map": "^0.6.1" @@ -4801,9 +4833,9 @@ } }, "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==", + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.4.tgz", + "integrity": "sha512-llS8qveOUX3wxHnSykP5hlYFFuMfJ9p5JvIyCiBgp7WTfl6K5ZcyHj8r8JsN/J6QODkAsRRCLIcTuOCu8etkUw==", "dev": true }, "node_modules/@types/webpack-sources": { @@ -5412,14 +5444,15 @@ } }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5429,14 +5462,15 @@ } }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5666,9 +5700,9 @@ } }, "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==", + "version": "2.1118.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1118.0.tgz", + "integrity": "sha512-R3g06c4RC0Gz/lwMA7wgC7+FwYf5vaO30sPIigoX5m6Tfb7tdzfCYD7pnpvkPRNUvWJ3f5kQk+pEeW25DstRrQ==", "dev": true, "dependencies": { "buffer": "4.9.2", @@ -5701,9 +5735,9 @@ "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==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dev": true, "dependencies": { "find-cache-dir": "^3.3.1", @@ -6600,6 +6634,15 @@ "isarray": "^1.0.0" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6633,23 +6676,23 @@ } }, "node_modules/c8": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.0.tgz", - "integrity": "sha512-XqPyj1uvlHMr+Y1IeRndC2X5P7iJzJlEJwBpCdBbq2JocXOgJfr+JVfJkyNMGROke5LfKrhSFXGFXnwnRJAUJw==", + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.2.tgz", + "integrity": "sha512-6ahJSrhS6TqSghHm+HnWt/8Y2+z0hM/FQyB1ybKhAR30+NYL9CTQ1uwHxuWw6U7BHlHv6wvhgOrH81I+lfCkxg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.2", + "@istanbuljs/schema": "^0.1.3", "find-up": "^5.0.0", "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.0.1", + "istanbul-lib-coverage": "^3.2.0", "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.0.2", - "rimraf": "^3.0.0", + "istanbul-reports": "^3.1.4", + "rimraf": "^3.0.2", "test-exclude": "^6.0.0", - "v8-to-istanbul": "^8.0.0", + "v8-to-istanbul": "^9.0.0", "yargs": "^16.2.0", - "yargs-parser": "^20.2.7" + "yargs-parser": "^20.2.9" }, "bin": { "c8": "bin/c8.js" @@ -6851,9 +6894,9 @@ } }, "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==", + "version": "1.0.30001332", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", + "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==", "funding": [ { "type": "opencollective", @@ -7175,9 +7218,9 @@ } }, "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==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", "dev": true, "dependencies": { "string-width": "^4.2.0" @@ -7186,7 +7229,7 @@ "node": "10.* || >= 12.*" }, "optionalDependencies": { - "colors": "1.4.0" + "@colors/colors": "1.5.0" } }, "node_modules/cliui": { @@ -7237,9 +7280,9 @@ } }, "node_modules/codemirror": { - "version": "5.65.2", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.2.tgz", - "integrity": "sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA==" + "version": "5.65.3", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.3.tgz", + "integrity": "sha512-kCC0iwGZOVZXHEKW3NDTObvM7pTIyowjty4BUqeREROc/3I6bWbgZDA3fGDwlA+rbgRjvnRnfqs9SfXynel1AQ==" }, "node_modules/collapse-white-space": { "version": "1.0.6", @@ -7326,16 +7369,6 @@ "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", @@ -7798,9 +7831,9 @@ } }, "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==", + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.2.tgz", + "integrity": "sha512-Z5I2vzDnEIqO2YhELVMFcL1An2CIsFe9Q7byZhs8c/QxummxZlAHw33TUHbIte987LkisOgL0LwQ1P9D6VISnA==", "dev": true, "hasInstallScript": true, "funding": { @@ -7809,12 +7842,12 @@ } }, "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==", + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.2.tgz", + "integrity": "sha512-Fns9lU06ZJ07pdfmPMu7OnkIKGPKDzXKIiuGlSvHHapwqMUF2QnnsWwtueFZtSyZEilP0o6iUeHQwpn7LxtLUw==", "dev": true, "dependencies": { - "browserslist": "^4.19.1", + "browserslist": "^4.20.2", "semver": "7.0.0" }, "funding": { @@ -7832,9 +7865,9 @@ } }, "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==", + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.2.tgz", + "integrity": "sha512-Lb+/XT4WC4PaCWWtZpNPaXmjiNDUe5CJuUtbkMrIM1kb1T/jJoAIp+bkVP/r5lHzMr+ZAAF8XHp7+my6Ol0ysQ==", "dev": true, "hasInstallScript": true, "funding": { @@ -8618,12 +8651,12 @@ } }, "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==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.7.tgz", + "integrity": "sha512-pVsUV6LcTXif7lvKKW9ZrmX+rGRzxkEdJuVJcp5ftUjWITgwam5LMZOgaTvUrWPkcORBey6he7JKb4XAJvrpKg==", "dev": true, "dependencies": { - "cssnano-preset-default": "^5.2.5", + "cssnano-preset-default": "^5.2.7", "lilconfig": "^2.0.3", "yaml": "^1.10.2" }, @@ -8639,12 +8672,12 @@ } }, "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==", + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz", + "integrity": "sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==", "dev": true, "dependencies": { - "css-declaration-sorter": "^6.0.3", + "css-declaration-sorter": "^6.2.2", "cssnano-utils": "^3.1.0", "postcss-calc": "^8.2.3", "postcss-colormin": "^5.3.0", @@ -8653,7 +8686,7 @@ "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-longhand": "^5.1.4", "postcss-merge-rules": "^5.1.1", "postcss-minify-font-values": "^5.1.0", "postcss-minify-gradients": "^5.1.1", @@ -8761,70 +8794,70 @@ } }, "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==" + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } }, "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==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" }, "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==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" }, "node_modules/d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", "dependencies": { - "d3-color": "1" + "d3-color": "1 - 2" } }, "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==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==" }, "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==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", + "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", "dependencies": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "^2.1.1", + "d3-time-format": "2 - 3" } }, "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==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz", + "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==", "dependencies": { - "d3-path": "1" + "d3-path": "1 - 2" } }, "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==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", + "dependencies": { + "d3-array": "2" + } }, "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==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", "dependencies": { - "d3-time": "1" + "d3-time": "1 - 2" } }, "node_modules/damerau-levenshtein": { @@ -8851,9 +8884,9 @@ "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==", + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.26.tgz", + "integrity": "sha512-YKw0BmJSWxkjtQsbgn6Q9CHSWB7DKMen8vKrgyC006zy0UZ6nWyGidB0IzZgqkVRkOglAeUaFtiRTeLyel72bg==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -8968,14 +9001,18 @@ } }, "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==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dependencies": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-property": { @@ -9225,13 +9262,13 @@ } }, "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==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-15.1.2.tgz", + "integrity": "sha512-EOec1LyJUuGRFg0LDa55rSRAUe97uNVKVkUo8iyvzQlcECYTuPblVQfRWXWj1OyPseFIeebWpNmKFy0h6BcF1A==", "dependencies": { - "@react-dnd/asap": "4.0.0", - "@react-dnd/invariant": "3.0.0", - "redux": "^4.1.1" + "@react-dnd/asap": "4.0.1", + "@react-dnd/invariant": "3.0.1", + "redux": "^4.1.2" } }, "node_modules/dns-equal": { @@ -9309,9 +9346,9 @@ } }, "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==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "funding": [ { "type": "github", @@ -9488,9 +9525,9 @@ } }, "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==" + "version": "1.4.117", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.117.tgz", + "integrity": "sha512-ypZHxY+Sf/PXu7LVN+xoeanyisnJeSOy8Ki439L/oLueZb4c72FI45zXcK3gPpmTwyufh9m6NnbMLXnJh/0Fxg==" }, "node_modules/element-resize-detector": { "version": "1.2.4", @@ -9699,9 +9736,9 @@ } }, "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==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", + "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", @@ -9715,7 +9752,7 @@ "is-callable": "^1.2.4", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", "is-weakref": "^1.0.2", "object-inspect": "^1.12.0", @@ -9763,6 +9800,15 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -9781,9 +9827,9 @@ } }, "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==", + "version": "0.10.61", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", + "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", "hasInstallScript": true, "dependencies": { "es6-iterator": "^2.0.3", @@ -10016,9 +10062,9 @@ } }, "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==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "dependencies": { "array-includes": "^3.1.4", @@ -10026,14 +10072,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "is-glob": "^4.0.3", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "engines": { "node": ">=4" @@ -10690,6 +10736,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-equals": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.4.tgz", + "integrity": "sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==" + }, "node_modules/fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -10802,9 +10853,9 @@ "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==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "dev": true }, "node_modules/figgy-pudding": { @@ -11318,9 +11369,9 @@ } }, "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==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.1.tgz", + "integrity": "sha512-x1wumpHOEf4gDROmKTaB6i4/Q6H3LwmjVO7fIX47vBwlZbtPjU33hgoMuD/Q/y6SU8bnuYSoN6ZQOLshGp0T/g==", "dev": true, "dependencies": { "@babel/code-frame": "^7.8.3", @@ -11443,9 +11494,9 @@ } }, "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==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -11656,10 +11707,9 @@ } }, "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, + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -11915,9 +11965,9 @@ } }, "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==" + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "node_modules/gud": { "version": "1.0.0", @@ -12006,9 +12056,9 @@ } }, "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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12051,6 +12101,17 @@ "node": ">=0.10.0" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -12584,11 +12645,14 @@ "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==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/html-void-elements": { @@ -13158,6 +13222,11 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, "node_modules/interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -13341,9 +13410,9 @@ } }, "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==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "dependencies": { "has": "^1.0.3" }, @@ -13529,9 +13598,9 @@ } }, "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==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" @@ -13622,10 +13691,13 @@ } }, "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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13934,9 +14006,9 @@ } }, "node_modules/jsbi": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.2.0.tgz", - "integrity": "sha512-JzhwPkH7Ra8O1b5uHmWxl6N8ZumqGvMXgpKcsq0/iWlnB+KkzbekCIcLl3hu3sFGTLNXAuXIqY4STtE0ZfT1zA==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==" }, "node_modules/jsesc": { "version": "2.5.2", @@ -14027,12 +14099,12 @@ } }, "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==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.2.tgz", + "integrity": "sha512-HDAyJ4MNQBboGpUnHAVUNJs6X0lh058s6FuixsFGP7MgJYpD6Vasd6nzSG5iIfXu1zAYlHJ/zsOKNlrenTUBnw==", "dev": true, "dependencies": { - "array-includes": "^3.1.3", + "array-includes": "^3.1.4", "object.assign": "^4.1.2" }, "engines": { @@ -14209,7 +14281,8 @@ "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=" + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true }, "node_modules/lodash.difference": { "version": "4.5.0", @@ -14228,11 +14301,6 @@ "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", @@ -14384,11 +14452,6 @@ "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", @@ -14743,14 +14806,15 @@ "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==", + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/minio/-/minio-7.0.27.tgz", + "integrity": "sha512-0eFFob4ZzqsK0lsuM2cRddDkdFrfQGKCpD77XbFzFClhQL4w85CXC3UwfIe0rBRmLzFMZ5Y9bph8Dpny/COrQQ==", "dev": true, "dependencies": { "async": "^3.1.0", "block-stream2": "^2.0.0", "browser-or-node": "^1.3.0", + "buffer-crc32": "^0.2.13", "crypto-browserify": "^3.12.0", "es6-error": "^4.1.1", "fast-xml-parser": "^3.17.5", @@ -14766,7 +14830,7 @@ "xml2js": "^0.4.15" }, "engines": { - "node": ">= 4" + "node": ">8 <16.8.0" } }, "node_modules/minipass": { @@ -14953,9 +15017,9 @@ } }, "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==", + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", + "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==", "engines": { "node": "*" } @@ -15280,9 +15344,9 @@ } }, "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==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", + "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==" }, "node_modules/normalize-package-data": { "version": "2.5.0", @@ -16376,12 +16440,12 @@ } }, "node_modules/polished": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/polished/-/polished-4.1.4.tgz", - "integrity": "sha512-Nq5Mbza+Auo7N3sQb1QMFaQiDO+4UexWuSGR7Cjb4Sw11SZIJcrrFtiZ+L0jT9MBsUsxDboHVASbCLbE1rnECg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz", + "integrity": "sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.16.7" + "@babel/runtime": "^7.17.8" }, "engines": { "node": ">=10" @@ -16412,9 +16476,9 @@ } }, "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==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "dependencies": { "lodash": "^4.17.14" @@ -16863,9 +16927,9 @@ } }, "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==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", + "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0", @@ -17632,9 +17696,9 @@ "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==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -17730,9 +17794,9 @@ } }, "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==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, "engines": { "node": ">= 6" @@ -17742,9 +17806,9 @@ } }, "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==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, "dependencies": { "domelementtype": "^2.0.1", @@ -17756,9 +17820,9 @@ } }, "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==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, "funding": [ { @@ -17923,9 +17987,9 @@ } }, "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==", + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.28.0.tgz", + "integrity": "sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==", "dev": true, "engines": { "node": ">=6" @@ -18554,13 +18618,13 @@ } }, "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==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-15.1.2.tgz", + "integrity": "sha512-EaSbMD9iFJDY/o48T3c8wn3uWU+2uxfFojhesZN3LhigJoAIvH2iOjxofSA9KbqhAKP6V9P853G6XG8JngKVtA==", "dependencies": { - "@react-dnd/invariant": "3.0.0", - "@react-dnd/shallowequal": "3.0.0", - "dnd-core": "15.1.1", + "@react-dnd/invariant": "3.0.1", + "@react-dnd/shallowequal": "3.0.1", + "dnd-core": "15.1.2", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" }, @@ -18583,11 +18647,11 @@ } }, "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==", + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-15.1.3.tgz", + "integrity": "sha512-HH/8nOEmrrcRGHMqJR91FOwhnLlx5SRLXmsQwZT3IPcBjx88WT+0pWC5A4tDOYDdoooh9k+KMPvWfxooR5TcOA==", "dependencies": { - "dnd-core": "15.1.1" + "dnd-core": "15.1.2" } }, "node_modules/react-docgen": { @@ -18687,9 +18751,9 @@ } }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", @@ -18699,8 +18763,8 @@ "shallowequal": "^1.1.0" }, "peerDependencies": { - "react": "^16.6.0 || ^17.0.0", - "react-dom": "^16.6.0 || ^17.0.0" + "react": "^16.6.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.6.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/react-highlight": { @@ -18842,17 +18906,17 @@ } }, "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==", + "version": "6.7.8", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-6.7.8.tgz", + "integrity": "sha512-0FaEcUBAbn+pq3PT5a9hHRebUfuS1SRLGLpIw8LydU7zX429I6XJgKerKAMPsJH0qWAl6o5bVKNqFJqr6tGPYw==", "dependencies": { - "lodash.debounce": "^4.0.8", - "lodash.throttle": "^4.1.1", - "prop-types": "^15.6.0", - "resize-observer-polyfill": "^1.5.0" + "@types/resize-observer-browser": "^0.1.6", + "lodash": "^4.17.21", + "resize-observer-polyfill": "^1.5.1" }, "peerDependencies": { - "react": "^0.14.7 || ^15.0.0 || ^16.0.0" + "react": "^16.0.0 || ^17.0.0", + "react-dom": "^16.0.0 || ^17.0.0" } }, "node_modules/react-router": { @@ -18945,18 +19009,18 @@ } }, "node_modules/react-smooth": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-1.0.6.tgz", - "integrity": "sha512-B2vL4trGpNSMSOzFiAul9kFAsxTukL9Wyy9EXtkQy3GJr6sZqW9e1nShdVOJ3hRYamPZ94O17r3Q0bjSw3UYtg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.0.tgz", + "integrity": "sha512-wK4dBBR6P21otowgMT9toZk+GngMplGS1O5gk+2WSiHEXIrQgDvhR5IIlT74Vtu//qpTcipkgo21dD7a7AUNxw==", "dependencies": { - "lodash": "~4.17.4", - "prop-types": "^15.6.0", + "fast-equals": "^2.0.0", "raf": "^3.4.0", - "react-transition-group": "^2.5.0" + "react-transition-group": "2.9.0" }, "peerDependencies": { - "react": "^15.0.0 || ^16.0.0", - "react-dom": "^15.0.0 || ^16.0.0" + "prop-types": "^15.6.0", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/react-smooth/node_modules/dom-helpers": { @@ -19215,25 +19279,31 @@ } }, "node_modules/recharts": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-1.8.5.tgz", - "integrity": "sha512-tM9mprJbXVEBxjM7zHsIy6Cc41oO/pVYqyAsOHLxlJrbNBuLs0PHB3iys2M+RqCF0//k8nJtZF6X6swSkWY3tg==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.1.9.tgz", + "integrity": "sha512-VozH5uznUvGqD7n224FGj7cmMAenlS0HPCs+7r2HeeHiQK6un6z0CTZfWVAB860xbcr4m+BN/EGMPZmYWd34Rg==", "dependencies": { + "@types/d3-interpolate": "^2.0.0", + "@types/d3-scale": "^3.0.0", + "@types/d3-shape": "^2.0.0", "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" + "d3-interpolate": "^2.0.0", + "d3-scale": "^3.0.0", + "d3-shape": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^6.6.3", + "react-smooth": "^2.0.0", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8" + }, + "engines": { + "node": ">=12" }, "peerDependencies": { - "react": "^15.0.0 || ^16.0.0", - "react-dom": "^15.0.0 || ^16.0.0" + "react": "^16.0.0 || ^17.0.0", + "react-dom": "^16.0.0 || ^17.0.0" } }, "node_modules/recharts-scale": { @@ -19244,40 +19314,29 @@ "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/recharts/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/recharts/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/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=", + "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": { - "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" + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.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==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", "dependencies": { "@babel/runtime": "^7.9.2" } @@ -19313,6 +19372,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/refractor/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/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -19337,9 +19405,9 @@ "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==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" @@ -19359,12 +19427,13 @@ } }, "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==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -19623,9 +19692,9 @@ } }, "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==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, "engines": { "node": ">= 6" @@ -19635,9 +19704,9 @@ } }, "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==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, "dependencies": { "domelementtype": "^2.0.1", @@ -19649,9 +19718,9 @@ } }, "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==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, "funding": [ { @@ -21640,20 +21709,6 @@ "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", @@ -22380,9 +22435,9 @@ } }, "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==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -22492,14 +22547,14 @@ } }, "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==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/typed-css-modules/-/typed-css-modules-0.7.1.tgz", + "integrity": "sha512-WaWHnLa3HBTCuOCDRDB3wfqoH9ouTxdLGQwzVBxV5x+nbPIr5ZkCrCb4yfU4D1bkzNprrTKTU2ETkYVOLGFmVA==", "dev": true, "dependencies": { "@types/css-modules-loader-core": "^1.1.0", - "camelcase": "^5.3.1", - "chalk": "^2.1.0", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", "chokidar": "^3.4.0", "css-modules-loader-core": "^1.1.0", "glob": "^7.1.2", @@ -22529,13 +22584,20 @@ "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==", + "node_modules/typed-css-modules/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": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/typed-css-modules/node_modules/cliui": { @@ -22562,6 +22624,15 @@ "node": ">=8" } }, + "node_modules/typed-css-modules/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/typed-css-modules/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -22598,6 +22669,18 @@ "node": ">=8" } }, + "node_modules/typed-css-modules/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/typed-css-modules/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -22647,6 +22730,15 @@ "node": ">=6" } }, + "node_modules/typed-css-modules/node_modules/yargs-parser/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-styles": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", @@ -22690,9 +22782,9 @@ } }, "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==", + "version": "3.15.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", + "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", "dev": true, "optional": true, "bin": { @@ -23135,11 +23227,11 @@ } }, "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==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -23224,14 +23316,14 @@ "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==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz", + "integrity": "sha512-HcvgY/xaRm7isYmyx+lFKA4uQmfUbN0J4M0nNItvzTvH/iQ9kW5j/t4YSR+Ge323/lrgDAWJoF46tzGQHwBHFw==", "dev": true, "dependencies": { + "@jridgewell/trace-mapping": "^0.3.7", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" + "convert-source-map": "^1.6.0" }, "engines": { "node": ">=10.12.0" @@ -25252,9 +25344,9 @@ } }, "node_modules/winston": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz", - "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", + "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==", "dev": true, "dependencies": { "@dabh/diagnostics": "^2.0.2", @@ -25529,9 +25621,9 @@ } }, "node_modules/yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", "dev": true, "dependencies": { "cliui": "^7.0.2", @@ -25653,32 +25745,32 @@ "dev": true }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.9.tgz", + "integrity": "sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.7", + "@babel/generator": "^7.17.9", "@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/helpers": "^7.17.9", + "@babel/parser": "^7.17.9", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", + "@babel/traverse": "^7.17.9", "@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", + "json5": "^2.2.1", "semver": "^6.3.0" } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.9.tgz", + "integrity": "sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ==", "dev": true, "requires": { "@babel/types": "^7.17.0", @@ -25726,15 +25818,15 @@ } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz", + "integrity": "sha512-kUjip3gruz6AJKOq5i3nC6CoCEEF/oHH3cp6tOZhB+IyyyPyW0g1Gfsxn3mkk6S08pIA2y8GQh609v9G/5sHVQ==", "dev": true, "requires": { "@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-function-name": "^7.17.9", + "@babel/helper-member-expression-to-functions": "^7.17.7", "@babel/helper-optimise-call-expression": "^7.16.7", "@babel/helper-replace-supers": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7" @@ -25785,23 +25877,13 @@ } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz", + "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.16.7", "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@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, - "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.17.0" } }, "@babel/helper-hoist-variables": { @@ -25938,20 +26020,20 @@ } }, "@babel/helpers": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.8.tgz", - "integrity": "sha512-QcL86FGxpfSJwGtAvv4iG93UL6bmqBdmoVY0CMCU2g+oD2ezQse3PT5Pa+jiD6LJndBQi0EDlpzOWNlLuhz5gw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.9.tgz", + "integrity": "sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q==", "dev": true, "requires": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.17.3", + "@babel/traverse": "^7.17.9", "@babel/types": "^7.17.0" } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", + "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -25960,9 +26042,9 @@ } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", + "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -26018,14 +26100,15 @@ } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.17.9.tgz", + "integrity": "sha512-EfH2LZ/vPa2wuPwJ26j+kYRkaubf89UlwxKXtxqEm57HrgSEYDB8t4swFP+p8LcI9yiP9ZRJJjo/58hS6BnaDA==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.17.6", + "@babel/helper-create-class-features-plugin": "^7.17.9", "@babel/helper-plugin-utils": "^7.16.7", "@babel/helper-replace-supers": "^7.16.7", + "@babel/helper-split-export-declaration": "^7.16.7", "@babel/plugin-syntax-decorators": "^7.17.0", "charcodes": "^0.2.0" } @@ -26507,9 +26590,9 @@ } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz", + "integrity": "sha512-2TBFd/r2I6VlYn0YRTz2JdazS+FoUuQ2rIFHoAxtyP/0G3D82SBLaRq9rnUkpqlLg03Byfl/+M32mpxjO6KaPw==", "dev": true, "requires": { "@babel/helper-module-transforms": "^7.17.7", @@ -26629,12 +26712,12 @@ } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.17.9.tgz", + "integrity": "sha512-Lc2TfbxR1HOyn/c6b4Y/b6NHoTb67n/IoWLxTu4kC7h4KQnWlhCq2S8Tx0t2SVvv5Uu87Hs+6JEJ5kt2tYGylQ==", "dev": true, "requires": { - "regenerator-transform": "^0.14.2" + "regenerator-transform": "^0.15.0" } }, "@babel/plugin-transform-reserved-words": { @@ -26867,17 +26950,17 @@ } }, "@babel/runtime": { - "version": "7.17.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz", - "integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", + "integrity": "sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@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==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.9.tgz", + "integrity": "sha512-WxYHHUWF2uZ7Hp1K+D1xQgbgkGUfA+5UPOegEXGt2Y5SMog/rYCVaifLZDbw8UkNXozEqqrZTy6bglL7xTaCOw==", "dev": true, "requires": { "core-js-pure": "^3.20.2", @@ -26896,18 +26979,18 @@ } }, "@babel/traverse": { - "version": "7.17.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz", - "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==", + "version": "7.17.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.9.tgz", + "integrity": "sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.3", + "@babel/generator": "^7.17.9", "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", + "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.3", + "@babel/parser": "^7.17.9", "@babel/types": "^7.17.0", "debug": "^4.1.0", "globals": "^11.1.0" @@ -27143,9 +27226,9 @@ "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==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -27303,9 +27386,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -27357,9 +27440,9 @@ } }, "@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==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.5.tgz", + "integrity": "sha512-RbG7h6TuP6nFFYKJwbcToA1rjC1FyPg25NR2noAZ0vKI+la01KTSRPkuVPE+U88jXv7javx2JHglUcL1MHcshQ==", "dev": true, "requires": { "ansi-html-community": "^0.0.8", @@ -27374,25 +27457,25 @@ } }, "@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==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", + "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==", "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==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", + "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==" }, "@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==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-3.0.1.tgz", + "integrity": "sha512-blqduwV86oiKw2Gr44wbe3pj3Z/OsXirc7ybCv9F/pLAR+Aih8F3rjeJzK0ANgtYKv5lCpkGVoZAeKitKDaD/g==" }, "@react-dnd/shallowequal": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-3.0.0.tgz", - "integrity": "sha512-1ELWQdJB2UrCXTKK5cCD9uGLLIwECLIEdttKA255owdpchtXohIjZBTlFJszwYi2ZKe2Do+QvUzsGyGCMNwbdw==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-3.0.1.tgz", + "integrity": "sha512-XjDVbs3ZU16CO1h5Q3Ew2RPJqmZBDE/EVf1LYp6ePEffs3V/MX9ZbL5bJr8qiK5SbGmUMuDoaFgyKacYz8prRA==" }, "@semantic-ui-react/event-stack": { "version": "3.1.2", @@ -27471,18 +27554,18 @@ "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==" }, "@storybook/addons": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.19.tgz", - "integrity": "sha512-QNyRYhpqmHV8oJxxTBdkRlLSbDFhpBvfvMfIrIT1UXb/eemdBZTaCGVvXZ9UixoEEI7f8VwAQ44IvkU5B1509w==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/addons/-/addons-6.4.22.tgz", + "integrity": "sha512-P/R+Jsxh7pawKLYo8MtE3QU/ilRFKbtCewV/T1o5U/gm8v7hKQdFz3YdRMAra4QuCY8bQIp7MKd2HrB5aH5a1A==", "dev": true, "requires": { - "@storybook/api": "6.4.19", - "@storybook/channels": "6.4.19", - "@storybook/client-logger": "6.4.19", - "@storybook/core-events": "6.4.19", + "@storybook/api": "6.4.22", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/router": "6.4.19", - "@storybook/theming": "6.4.19", + "@storybook/router": "6.4.22", + "@storybook/theming": "6.4.22", "@types/webpack-env": "^1.16.0", "core-js": "^3.8.2", "global": "^4.4.0", @@ -27490,18 +27573,18 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.4.22.tgz", + "integrity": "sha512-lAVI3o2hKupYHXFTt+1nqFct942up5dHH6YD7SZZJGyW21dwKC3HK1IzCsTawq3fZAKkgWFgmOO649hKk60yKg==", "dev": true, "requires": { - "@storybook/channels": "6.4.19", - "@storybook/client-logger": "6.4.19", - "@storybook/core-events": "6.4.19", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/router": "6.4.19", + "@storybook/router": "6.4.22", "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.4.19", + "@storybook/theming": "6.4.22", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", @@ -27515,9 +27598,9 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack4/-/builder-webpack4-6.4.22.tgz", + "integrity": "sha512-A+GgGtKGnBneRFSFkDarUIgUTI8pYFdLmUVKEAGdh2hL+vLXAz9A46sEY7C8LQ85XWa8TKy3OTDxqR4+4iWj3A==", "dev": true, "requires": { "@babel/core": "^7.12.10", @@ -27541,22 +27624,22 @@ "@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/addons": "6.4.22", + "@storybook/api": "6.4.22", + "@storybook/channel-postmessage": "6.4.22", + "@storybook/channels": "6.4.22", + "@storybook/client-api": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/components": "6.4.22", + "@storybook/core-common": "6.4.22", + "@storybook/core-events": "6.4.22", + "@storybook/node-logger": "6.4.22", + "@storybook/preview-web": "6.4.22", + "@storybook/router": "6.4.22", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.19", - "@storybook/theming": "6.4.19", - "@storybook/ui": "6.4.19", + "@storybook/store": "6.4.22", + "@storybook/theming": "6.4.22", + "@storybook/ui": "6.4.22", "@types/node": "^14.0.10", "@types/webpack": "^4.41.26", "autoprefixer": "^9.8.6", @@ -27854,9 +27937,9 @@ }, "dependencies": { "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -27912,14 +27995,14 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.4.22.tgz", + "integrity": "sha512-gt+0VZLszt2XZyQMh8E94TqjHZ8ZFXZ+Lv/Mmzl0Yogsc2H+6VzTTQO4sv0IIx6xLbpgG72g5cr8VHsxW5kuDQ==", "dev": true, "requires": { - "@storybook/channels": "6.4.19", - "@storybook/client-logger": "6.4.19", - "@storybook/core-events": "6.4.19", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "core-js": "^3.8.2", "global": "^4.4.0", "qs": "^6.10.0", @@ -27927,22 +28010,22 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/channel-websocket/-/channel-websocket-6.4.22.tgz", + "integrity": "sha512-Bm/FcZ4Su4SAK5DmhyKKfHkr7HiHBui6PNutmFkASJInrL9wBduBfN8YQYaV7ztr8ezoHqnYRx8sj28jpwa6NA==", "dev": true, "requires": { - "@storybook/channels": "6.4.19", - "@storybook/client-logger": "6.4.19", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", "core-js": "^3.8.2", "global": "^4.4.0", "telejson": "^5.3.2" } }, "@storybook/channels": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.19.tgz", - "integrity": "sha512-EwyoncFvTfmIlfsy8jTfayCxo2XchPkZk/9txipugWSmc057HdklMKPLOHWP0z5hLH0IbVIKXzdNISABm36jwQ==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-6.4.22.tgz", + "integrity": "sha512-cfR74tu7MLah1A8Rru5sak71I+kH2e/sY6gkpVmlvBj4hEmdZp4Puj9PTeaKcMXh9DgIDPNA5mb8yvQH6VcyxQ==", "dev": true, "requires": { "core-js": "^3.8.2", @@ -27951,18 +28034,18 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/client-api/-/client-api-6.4.22.tgz", + "integrity": "sha512-sO6HJNtrrdit7dNXQcZMdlmmZG1k6TswH3gAyP/DoYajycrTwSJ6ovkarzkO+0QcJ+etgra4TEdTIXiGHBMe/A==", "dev": true, "requires": { - "@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/addons": "6.4.22", + "@storybook/channel-postmessage": "6.4.22", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/store": "6.4.19", + "@storybook/store": "6.4.22", "@types/qs": "^6.9.5", "@types/webpack-env": "^1.16.0", "core-js": "^3.8.2", @@ -27979,9 +28062,9 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.4.22.tgz", + "integrity": "sha512-LXhxh/lcDsdGnK8kimqfhu3C0+D2ylCSPPQNbU0IsLRmTfbpQYMdyl0XBjPdHiRVwlL7Gkw5OMjYemQgJ02zlw==", "dev": true, "requires": { "core-js": "^3.8.2", @@ -27989,15 +28072,15 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-6.4.22.tgz", + "integrity": "sha512-dCbXIJF9orMvH72VtAfCQsYbe57OP7fAADtR6YTwfCw9Sm1jFuZr8JbblQ1HcrXEoJG21nOyad3Hm5EYVb/sBw==", "dev": true, "requires": { "@popperjs/core": "^2.6.0", - "@storybook/client-logger": "6.4.19", + "@storybook/client-logger": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/theming": "6.4.19", + "@storybook/theming": "6.4.22", "@types/color-convert": "^2.0.0", "@types/overlayscrollbars": "^1.12.0", "@types/react-syntax-highlighter": "11.0.5", @@ -28021,31 +28104,31 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core/-/core-6.4.22.tgz", + "integrity": "sha512-KZYJt7GM5NgKFXbPRZZZPEONZ5u/tE/cRbMdkn/zWN3He8+VP+65/tz8hbriI/6m91AWVWkBKrODSkeq59NgRA==", "dev": true, "requires": { - "@storybook/core-client": "6.4.19", - "@storybook/core-server": "6.4.19" + "@storybook/core-client": "6.4.22", + "@storybook/core-server": "6.4.22" } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-6.4.22.tgz", + "integrity": "sha512-uHg4yfCBeM6eASSVxStWRVTZrAnb4FT6X6v/xDqr4uXCpCttZLlBzrSDwPBLNNLtCa7ntRicHM8eGKIOD5lMYQ==", "dev": true, "requires": { - "@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/addons": "6.4.22", + "@storybook/channel-postmessage": "6.4.22", + "@storybook/channel-websocket": "6.4.22", + "@storybook/client-api": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/preview-web": "6.4.19", - "@storybook/store": "6.4.19", - "@storybook/ui": "6.4.19", + "@storybook/preview-web": "6.4.22", + "@storybook/store": "6.4.22", + "@storybook/ui": "6.4.22", "airbnb-js-shims": "^2.2.1", "ansi-to-html": "^0.6.11", "core-js": "^3.8.2", @@ -28059,9 +28142,9 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-6.4.22.tgz", + "integrity": "sha512-PD3N/FJXPNRHeQS2zdgzYFtqPLdi3MLwAicbnw+U3SokcsspfsAuyYHZOYZgwO8IAEKy6iCc7TpBdiSJZ/vAKQ==", "dev": true, "requires": { "@babel/core": "^7.12.10", @@ -28085,7 +28168,7 @@ "@babel/preset-react": "^7.12.10", "@babel/preset-typescript": "^7.12.7", "@babel/register": "^7.12.1", - "@storybook/node-logger": "6.4.19", + "@storybook/node-logger": "6.4.22", "@storybook/semver": "^7.3.2", "@types/node": "^14.0.10", "@types/pretty-hrtime": "^1.0.0", @@ -28184,31 +28267,31 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-6.4.22.tgz", + "integrity": "sha512-5GYY5+1gd58Gxjqex27RVaX6qbfIQmJxcbzbNpXGNSqwqAuIIepcV1rdCVm6I4C3Yb7/AQ3cN5dVbf33QxRIwA==", "dev": true, "requires": { "core-js": "^3.8.2" } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-6.4.22.tgz", + "integrity": "sha512-wFh3e2fa0un1d4+BJP+nd3FVWUO7uHTqv3OGBfOmzQMKp4NU1zaBNdSQG7Hz6mw0fYPBPZgBjPfsJRwIYLLZyw==", "dev": true, "requires": { "@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/builder-webpack4": "6.4.22", + "@storybook/core-client": "6.4.22", + "@storybook/core-common": "6.4.22", + "@storybook/core-events": "6.4.22", "@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/csf-tools": "6.4.22", + "@storybook/manager-webpack4": "6.4.22", + "@storybook/node-logger": "6.4.22", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.19", + "@storybook/store": "6.4.22", "@types/node": "^14.0.10", "@types/node-fetch": "^2.5.7", "@types/pretty-hrtime": "^1.0.0", @@ -28300,9 +28383,9 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-6.4.22.tgz", + "integrity": "sha512-LMu8MZAiQspJAtMBLU2zitsIkqQv7jOwX7ih5JrXlyaDticH7l2j6Q+1mCZNWUOiMTizj0ivulmUsSaYbpToSw==", "dev": true, "requires": { "@babel/core": "^7.12.10", @@ -28325,20 +28408,20 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/manager-webpack4/-/manager-webpack4-6.4.22.tgz", + "integrity": "sha512-nzhDMJYg0vXdcG0ctwE6YFZBX71+5NYaTGkxg3xT7gbgnP1YFXn9gVODvgq3tPb3gcRapjyOIxUa20rV+r8edA==", "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.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", + "@storybook/addons": "6.4.22", + "@storybook/core-client": "6.4.22", + "@storybook/core-common": "6.4.22", + "@storybook/node-logger": "6.4.22", + "@storybook/theming": "6.4.22", + "@storybook/ui": "6.4.22", "@types/node": "^14.0.10", "@types/webpack": "^4.41.26", "babel-loader": "^8.0.0", @@ -28465,9 +28548,9 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-6.4.22.tgz", + "integrity": "sha512-sUXYFqPxiqM7gGH7gBXvO89YEO42nA4gBicJKZjj9e+W4QQLrftjF9l+mAw2K0mVE10Bn7r4pfs5oEZ0aruyyA==", "dev": true, "requires": { "@types/npmlog": "^4.1.2", @@ -28514,17 +28597,17 @@ } }, "@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==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/preview-web/-/preview-web-6.4.22.tgz", + "integrity": "sha512-sWS+sgvwSvcNY83hDtWUUL75O2l2LY/GTAS0Zp2dh3WkObhtuJ/UehftzPZlZmmv7PCwhb4Q3+tZDKzMlFxnKQ==", "dev": true, "requires": { - "@storybook/addons": "6.4.19", - "@storybook/channel-postmessage": "6.4.19", - "@storybook/client-logger": "6.4.19", - "@storybook/core-events": "6.4.19", + "@storybook/addons": "6.4.22", + "@storybook/channel-postmessage": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/store": "6.4.19", + "@storybook/store": "6.4.22", "ansi-to-html": "^0.6.11", "core-js": "^3.8.2", "global": "^4.4.0", @@ -28538,22 +28621,22 @@ } }, "@storybook/react": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-6.4.19.tgz", - "integrity": "sha512-5b3i8jkVrjQGmcxxxXwCduHPIh+cluWkfeweKeQOe+lW4BR8fuUICo3AMLrYPAtB/UcaJyYkIYmTvF2mkfepFA==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-6.4.22.tgz", + "integrity": "sha512-5BFxtiguOcePS5Ty/UoH7C6odmvBYIZutfiy4R3Ua6FYmtxac5vP9r5KjCz1IzZKT8mCf4X+PuK1YvDrPPROgQ==", "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.19", - "@storybook/core": "6.4.19", - "@storybook/core-common": "6.4.19", + "@storybook/addons": "6.4.22", + "@storybook/core": "6.4.22", + "@storybook/core-common": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", - "@storybook/node-logger": "6.4.19", + "@storybook/node-logger": "6.4.22", "@storybook/react-docgen-typescript-plugin": "1.0.2-canary.253f8c1.0", "@storybook/semver": "^7.3.2", - "@storybook/store": "6.4.19", + "@storybook/store": "6.4.22", "@types/webpack-env": "^1.16.0", "babel-plugin-add-react-displayname": "^0.0.5", "babel-plugin-named-asset-import": "^0.3.1", @@ -28650,12 +28733,12 @@ } }, "@storybook/router": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.4.19.tgz", - "integrity": "sha512-KWWwIzuyeEIWVezkCihwY2A76Il9tUNg0I410g9qT7NrEsKyqXGRYOijWub7c1GGyNjLqz0jtrrehtixMcJkuA==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-6.4.22.tgz", + "integrity": "sha512-zeuE8ZgFhNerQX8sICQYNYL65QEi3okyzw7ynF58Ud6nRw4fMxSOHcj2T+nZCIU5ufozRL4QWD/Rg9P2s/HtLw==", "dev": true, "requires": { - "@storybook/client-logger": "6.4.19", + "@storybook/client-logger": "6.4.22", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", @@ -28669,9 +28752,9 @@ }, "dependencies": { "react-router": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.2.tgz", - "integrity": "sha512-/MbxyLzd7Q7amp4gDOGaYvXwhEojkJD5BtExkuKmj39VEE0m3l/zipf6h2WIB2jyAO0lI6NGETh4RDcktRm4AQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz", + "integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==", "dev": true, "requires": { "history": "^5.2.0" @@ -28689,13 +28772,13 @@ } }, "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==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz", + "integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==", "dev": true, "requires": { "history": "^5.2.0", - "react-router": "6.2.2" + "react-router": "6.3.0" }, "dependencies": { "history": { @@ -28752,14 +28835,14 @@ } }, "@storybook/store": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/store/-/store-6.4.19.tgz", - "integrity": "sha512-N9/ZjemRHGfT3InPIbqQqc6snkcfnf3Qh9oOr0smbfaVGJol//KOX65kzzobtzFcid0WxtTDZ3HmgFVH+GvuhQ==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/store/-/store-6.4.22.tgz", + "integrity": "sha512-lrmcZtYJLc2emO+1l6AG4Txm9445K6Pyv9cGAuhOJ9Kks0aYe0YtvMkZVVry0RNNAIv6Ypz72zyKc/QK+tZLAQ==", "dev": true, "requires": { - "@storybook/addons": "6.4.19", - "@storybook/client-logger": "6.4.19", - "@storybook/core-events": "6.4.19", + "@storybook/addons": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/core-events": "6.4.22", "@storybook/csf": "0.0.2--canary.87bc651.0", "core-js": "^3.8.2", "fast-deep-equal": "^3.1.3", @@ -28783,15 +28866,15 @@ } }, "@storybook/theming": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.19.tgz", - "integrity": "sha512-V4pWmTvAxmbHR6B3jA4hPkaxZPyExHvCToy7b76DpUTpuHihijNDMAn85KhOQYIeL9q14zP/aiz899tOHsOidg==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-6.4.22.tgz", + "integrity": "sha512-NVMKH/jxSPtnMTO4VCN1k47uztq+u9fWv4GSnzq/eezxdGg9ceGL4/lCrNGoNajht9xbrsZ4QvsJ/V2sVGM8wA==", "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.19", + "@storybook/client-logger": "6.4.22", "core-js": "^3.8.2", "deep-object-diff": "^1.1.0", "emotion-theming": "^10.0.27", @@ -28803,21 +28886,21 @@ } }, "@storybook/ui": { - "version": "6.4.19", - "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.19.tgz", - "integrity": "sha512-gFwdn5LA2U6oQ4bfUFLyHZnNasGQ01YVdwjbi+l6yjmnckBNtZfJoVTZ1rzGUbxSE9rK48InJRU+latTsr7xAg==", + "version": "6.4.22", + "resolved": "https://registry.npmjs.org/@storybook/ui/-/ui-6.4.22.tgz", + "integrity": "sha512-UVjMoyVsqPr+mkS1L7m30O/xrdIEgZ5SCWsvqhmyMUok3F3tRB+6M+OA5Yy+cIVfvObpA7MhxirUT1elCGXsWQ==", "dev": true, "requires": { "@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/addons": "6.4.22", + "@storybook/api": "6.4.22", + "@storybook/channels": "6.4.22", + "@storybook/client-logger": "6.4.22", + "@storybook/components": "6.4.22", + "@storybook/core-events": "6.4.22", + "@storybook/router": "6.4.22", "@storybook/semver": "^7.3.2", - "@storybook/theming": "6.4.19", + "@storybook/theming": "6.4.22", "copy-to-clipboard": "^3.3.1", "core-js": "^3.8.2", "core-js-pure": "^3.8.2", @@ -28873,6 +28956,45 @@ "postcss": "7.x.x" } }, + "@types/d3-color": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.3.tgz", + "integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w==" + }, + "@types/d3-interpolate": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-2.0.2.tgz", + "integrity": "sha512-lElyqlUfIPyWG/cD475vl6msPL4aMU7eJvx1//Q177L8mdXoVPFl1djIESF2FKnc0NyaHvQlJpWwKJYwAhUoCw==", + "requires": { + "@types/d3-color": "^2" + } + }, + "@types/d3-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.2.tgz", + "integrity": "sha512-3YHpvDw9LzONaJzejXLOwZ3LqwwkoXb9LI2YN7Hbd6pkGo5nIlJ09ul4bQhBN4hQZJKmUpX8HkVqbzgUKY48cg==" + }, + "@types/d3-scale": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz", + "integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==", + "requires": { + "@types/d3-time": "^2" + } + }, + "@types/d3-shape": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.1.3.tgz", + "integrity": "sha512-HAhCel3wP93kh4/rq+7atLdybcESZ5bRHDEZUojClyZWsRuEMo3A52NGYJSh48SxfxEU6RZIVbZL2YFZ2OAlzQ==", + "requires": { + "@types/d3-path": "^2" + } + }, + "@types/d3-time": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==" + }, "@types/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", @@ -28938,10 +29060,10 @@ "dev": true }, "@types/node": { - "version": "14.18.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.12.tgz", - "integrity": "sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==", - "devOptional": true + "version": "14.18.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.13.tgz", + "integrity": "sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA==", + "dev": true }, "@types/node-fetch": { "version": "2.6.1", @@ -28990,10 +29112,10 @@ "dev": true }, "@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==", - "devOptional": true + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true }, "@types/q": { "version": "1.5.5", @@ -29008,10 +29130,10 @@ "dev": true }, "@types/react": { - "version": "17.0.43", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.43.tgz", - "integrity": "sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A==", - "devOptional": true, + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.5.tgz", + "integrity": "sha512-UPxNGInDCIKlfqBrm8LDXYWNfLHwIdisWcsH5GpMyGjhEDLFgTtlRBaoWuCua9HcyuE0rMkmAeZ3FXV1pYLIYQ==", + "dev": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -29022,7 +29144,7 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "devOptional": true + "dev": true } } }, @@ -29035,11 +29157,16 @@ "@types/react": "*" } }, + "@types/resize-observer-browser": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz", + "integrity": "sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==" + }, "@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "devOptional": true + "dev": true }, "@types/source-list-map": { "version": "0.1.2", @@ -29054,9 +29181,9 @@ "dev": true }, "@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==", + "version": "3.13.2", + "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.2.tgz", + "integrity": "sha512-/xFrPIo+4zOeNGtVMbf9rUm0N+i4pDf1ynExomqtokIJmVzR3962lJ1UE+MmexMkA0cmN9oTzg5Xcbwge0Ij2Q==", "dev": true, "requires": { "source-map": "^0.6.1" @@ -29099,9 +29226,9 @@ } }, "@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==", + "version": "1.16.4", + "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.16.4.tgz", + "integrity": "sha512-llS8qveOUX3wxHnSykP5hlYFFuMfJ9p5JvIyCiBgp7WTfl6K5ZcyHj8r8JsN/J6QODkAsRRCLIcTuOCu8etkUw==", "dev": true }, "@types/webpack-sources": { @@ -29404,15 +29531,13 @@ "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, - "requires": {} + "dev": true }, "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, - "requires": {} + "dev": true }, "ansi-align": { "version": "3.0.1", @@ -29608,25 +29733,27 @@ "dev": true }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, "requires": { "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", + "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", "dev": true, "requires": { - "call-bind": "^1.0.0", + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.19.0" + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, "array.prototype.map": { @@ -29815,9 +29942,9 @@ "dev": true }, "aws-sdk": { - "version": "2.1102.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1102.0.tgz", - "integrity": "sha512-MMOncE8IG3Dop3WPza6ryTAEz413ftn/MtDO7ouessb3ljlg5BfqRkTe/rhPH5svqEqJvlh7qHnK0VjgJwmLTQ==", + "version": "2.1118.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1118.0.tgz", + "integrity": "sha512-R3g06c4RC0Gz/lwMA7wgC7+FwYf5vaO30sPIigoX5m6Tfb7tdzfCYD7pnpvkPRNUvWJ3f5kQk+pEeW25DstRrQ==", "dev": true, "requires": { "buffer": "4.9.2", @@ -29844,9 +29971,9 @@ "dev": true }, "babel-loader": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz", - "integrity": "sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dev": true, "requires": { "find-cache-dir": "^3.3.1", @@ -30040,8 +30167,7 @@ "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, - "requires": {} + "dev": true }, "babel-plugin-polyfill-corejs2": { "version": "0.3.1", @@ -30565,6 +30691,12 @@ "isarray": "^1.0.0" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "dev": true + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -30595,23 +30727,23 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "c8": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.0.tgz", - "integrity": "sha512-XqPyj1uvlHMr+Y1IeRndC2X5P7iJzJlEJwBpCdBbq2JocXOgJfr+JVfJkyNMGROke5LfKrhSFXGFXnwnRJAUJw==", + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.2.tgz", + "integrity": "sha512-6ahJSrhS6TqSghHm+HnWt/8Y2+z0hM/FQyB1ybKhAR30+NYL9CTQ1uwHxuWw6U7BHlHv6wvhgOrH81I+lfCkxg==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.2", + "@istanbuljs/schema": "^0.1.3", "find-up": "^5.0.0", "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.0.1", + "istanbul-lib-coverage": "^3.2.0", "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.0.2", - "rimraf": "^3.0.0", + "istanbul-reports": "^3.1.4", + "rimraf": "^3.0.2", "test-exclude": "^6.0.0", - "v8-to-istanbul": "^8.0.0", + "v8-to-istanbul": "^9.0.0", "yargs": "^16.2.0", - "yargs-parser": "^20.2.7" + "yargs-parser": "^20.2.9" }, "dependencies": { "rimraf": { @@ -30772,9 +30904,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001322", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001322.tgz", - "integrity": "sha512-neRmrmIrCGuMnxGSoh+x7zYtQFFgnSY2jaomjU56sCkTA6JINqQrxutF459JpWcWRajvoyn95sOXq4Pqrnyjew==" + "version": "1.0.30001332", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz", + "integrity": "sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -30875,8 +31007,7 @@ "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, - "requires": {} + "dev": true }, "class-utils": { "version": "0.3.6", @@ -31008,12 +31139,12 @@ "dev": true }, "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==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", "dev": true, "requires": { - "colors": "1.4.0", + "@colors/colors": "1.5.0", "string-width": "^4.2.0" } }, @@ -31056,9 +31187,9 @@ } }, "codemirror": { - "version": "5.65.2", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.2.tgz", - "integrity": "sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA==" + "version": "5.65.3", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.3.tgz", + "integrity": "sha512-kCC0iwGZOVZXHEKW3NDTObvM7pTIyowjty4BUqeREROc/3I6bWbgZDA3fGDwlA+rbgRjvnRnfqs9SfXynel1AQ==" }, "collapse-white-space": { "version": "1.0.6", @@ -31134,13 +31265,6 @@ "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", "dev": true }, - "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 - }, "colorspace": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", @@ -31525,18 +31649,18 @@ } }, "core-js": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz", - "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==", + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.2.tgz", + "integrity": "sha512-Z5I2vzDnEIqO2YhELVMFcL1An2CIsFe9Q7byZhs8c/QxummxZlAHw33TUHbIte987LkisOgL0LwQ1P9D6VISnA==", "dev": true }, "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==", + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.22.2.tgz", + "integrity": "sha512-Fns9lU06ZJ07pdfmPMu7OnkIKGPKDzXKIiuGlSvHHapwqMUF2QnnsWwtueFZtSyZEilP0o6iUeHQwpn7LxtLUw==", "dev": true, "requires": { - "browserslist": "^4.19.1", + "browserslist": "^4.20.2", "semver": "7.0.0" }, "dependencies": { @@ -31549,9 +31673,9 @@ } }, "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==", + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.2.tgz", + "integrity": "sha512-Lb+/XT4WC4PaCWWtZpNPaXmjiNDUe5CJuUtbkMrIM1kb1T/jJoAIp+bkVP/r5lHzMr+ZAAF8XHp7+my6Ol0ysQ==", "dev": true }, "core-util-is": { @@ -31942,8 +32066,7 @@ "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, - "requires": {} + "dev": true }, "css-loader": { "version": "3.6.0", @@ -32203,23 +32326,23 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, "cssnano": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.5.tgz", - "integrity": "sha512-VZO1e+bRRVixMeia1zKagrv0lLN1B/r/u12STGNNUFxnp97LIFgZHQa0JxqlwEkvzUyA9Oz/WnCTAFkdEbONmg==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.7.tgz", + "integrity": "sha512-pVsUV6LcTXif7lvKKW9ZrmX+rGRzxkEdJuVJcp5ftUjWITgwam5LMZOgaTvUrWPkcORBey6he7JKb4XAJvrpKg==", "dev": true, "requires": { - "cssnano-preset-default": "^5.2.5", + "cssnano-preset-default": "^5.2.7", "lilconfig": "^2.0.3", "yaml": "^1.10.2" } }, "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==", + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.7.tgz", + "integrity": "sha512-JiKP38ymZQK+zVKevphPzNSGHSlTI+AOwlasoSRtSVMUU285O7/6uZyd5NbW92ZHp41m0sSHe6JoZosakj63uA==", "dev": true, "requires": { - "css-declaration-sorter": "^6.0.3", + "css-declaration-sorter": "^6.2.2", "cssnano-utils": "^3.1.0", "postcss-calc": "^8.2.3", "postcss-colormin": "^5.3.0", @@ -32228,7 +32351,7 @@ "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-longhand": "^5.1.4", "postcss-merge-rules": "^5.1.1", "postcss-minify-font-values": "^5.1.0", "postcss-minify-gradients": "^5.1.1", @@ -32254,8 +32377,7 @@ "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, - "requires": {} + "dev": true }, "csso": { "version": "4.2.0", @@ -32318,70 +32440,70 @@ } }, "d3-array": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" - }, - "d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "requires": { + "internmap": "^1.0.0" + } }, "d3-color": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", - "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" }, "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==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" }, "d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", "requires": { - "d3-color": "1" + "d3-color": "1 - 2" } }, "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==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==" }, "d3-scale": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", - "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", + "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", "requires": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "^2.1.1", + "d3-time-format": "2 - 3" } }, "d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz", + "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==", "requires": { - "d3-path": "1" + "d3-path": "1 - 2" } }, "d3-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", + "requires": { + "d3-array": "2" + } }, "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==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", "requires": { - "d3-time": "1" + "d3-time": "1 - 2" } }, "damerau-levenshtein": { @@ -32401,9 +32523,9 @@ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" }, "deasync": { - "version": "0.1.24", - "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.24.tgz", - "integrity": "sha512-i98vg42xNfRZCymummMAN0rIcQ1gZFinSe3btvPIvy6JFTaeHcumeKybRo2HTv86nasfmT0nEgAn2ggLZhOCVA==", + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/deasync/-/deasync-0.1.26.tgz", + "integrity": "sha512-YKw0BmJSWxkjtQsbgn6Q9CHSWB7DKMen8vKrgyC006zy0UZ6nWyGidB0IzZgqkVRkOglAeUaFtiRTeLyel72bg==", "dev": true, "requires": { "bindings": "^1.5.0", @@ -32491,11 +32613,12 @@ } }, "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==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "requires": { - "object-keys": "^1.0.12" + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, "define-property": { @@ -32703,13 +32826,13 @@ } }, "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==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-15.1.2.tgz", + "integrity": "sha512-EOec1LyJUuGRFg0LDa55rSRAUe97uNVKVkUo8iyvzQlcECYTuPblVQfRWXWj1OyPseFIeebWpNmKFy0h6BcF1A==", "requires": { - "@react-dnd/asap": "4.0.0", - "@react-dnd/invariant": "3.0.0", - "redux": "^4.1.1" + "@react-dnd/asap": "4.0.1", + "@react-dnd/invariant": "3.0.1", + "redux": "^4.1.2" } }, "dns-equal": { @@ -32786,9 +32909,9 @@ }, "dependencies": { "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" } } }, @@ -32953,9 +33076,9 @@ "dev": true }, "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==" + "version": "1.4.117", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.117.tgz", + "integrity": "sha512-ypZHxY+Sf/PXu7LVN+xoeanyisnJeSOy8Ki439L/oLueZb4c72FI45zXcK3gPpmTwyufh9m6NnbMLXnJh/0Fxg==" }, "element-resize-detector": { "version": "1.2.4", @@ -33063,8 +33186,7 @@ "ws": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" } } }, @@ -33129,9 +33251,9 @@ } }, "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==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", + "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -33145,7 +33267,7 @@ "is-callable": "^1.2.4", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.1", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", "is-weakref": "^1.0.2", "object-inspect": "^1.12.0", @@ -33186,6 +33308,15 @@ } } }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -33198,9 +33329,9 @@ } }, "es5-ext": { - "version": "0.10.59", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.59.tgz", - "integrity": "sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw==", + "version": "0.10.61", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz", + "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==", "requires": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", @@ -33380,9 +33511,9 @@ } }, "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==", + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", "dev": true, "requires": { "array-includes": "^3.1.4", @@ -33390,14 +33521,14 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "is-glob": "^4.0.3", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "object.values": "^1.1.5", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.12.0" + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" }, "dependencies": { "debug": { @@ -33924,6 +34055,11 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-equals": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.4.tgz", + "integrity": "sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==" + }, "fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -34019,9 +34155,9 @@ "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" }, "fecha": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", - "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "dev": true }, "figgy-pudding": { @@ -34442,9 +34578,9 @@ } }, "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==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.1.tgz", + "integrity": "sha512-x1wumpHOEf4gDROmKTaB6i4/Q6H3LwmjVO7fIX47vBwlZbtPjU33hgoMuD/Q/y6SU8bnuYSoN6ZQOLshGp0T/g==", "dev": true, "requires": { "@babel/code-frame": "^7.8.3", @@ -34521,9 +34657,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -34695,10 +34831,9 @@ } }, "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 + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" }, "fuse.js": { "version": "3.6.1", @@ -34886,9 +35021,9 @@ } }, "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==" + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "gud": { "version": "1.0.0", @@ -34958,9 +35093,9 @@ } }, "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true }, "has-cors": { @@ -34993,6 +35128,14 @@ } } }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -35431,9 +35574,9 @@ } }, "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==" }, "html-void-elements": { "version": "1.0.5", @@ -35905,6 +36048,11 @@ "side-channel": "^1.0.4" } }, + "internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, "interpret": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", @@ -36024,9 +36172,9 @@ "dev": true }, "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==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", "requires": { "has": "^1.0.3" } @@ -36147,9 +36295,9 @@ "dev": true }, "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==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "requires": { "has-tostringtag": "^1.0.0" @@ -36210,10 +36358,13 @@ "dev": true }, "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 + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } }, "is-stream": { "version": "1.1.0", @@ -36437,9 +36588,9 @@ } }, "jsbi": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.2.0.tgz", - "integrity": "sha512-JzhwPkH7Ra8O1b5uHmWxl6N8ZumqGvMXgpKcsq0/iWlnB+KkzbekCIcLl3hu3sFGTLNXAuXIqY4STtE0ZfT1zA==" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==" }, "jsesc": { "version": "2.5.2", @@ -36512,12 +36663,12 @@ } }, "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==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.2.tgz", + "integrity": "sha512-HDAyJ4MNQBboGpUnHAVUNJs6X0lh058s6FuixsFGP7MgJYpD6Vasd6nzSG5iIfXu1zAYlHJ/zsOKNlrenTUBnw==", "dev": true, "requires": { - "array-includes": "^3.1.3", + "array-includes": "^3.1.4", "object.assign": "^4.1.2" } }, @@ -36658,7 +36809,8 @@ "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true }, "lodash.difference": { "version": "4.5.0", @@ -36677,11 +36829,6 @@ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, - "lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=" - }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -36794,13 +36941,7 @@ "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, - "requires": {} - }, - "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==" + "dev": true }, "md5-file": { "version": "5.0.0", @@ -37090,14 +37231,15 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "minio": { - "version": "7.0.26", - "resolved": "https://registry.npmjs.org/minio/-/minio-7.0.26.tgz", - "integrity": "sha512-knutnEZZMIUB/Xln6psVDrqObFKXDcF9m4IfFIX+zgDHYg3AlcF88DY1wdgg7bUkf+uU8iHkzP2q5CXAhia73w==", + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/minio/-/minio-7.0.27.tgz", + "integrity": "sha512-0eFFob4ZzqsK0lsuM2cRddDkdFrfQGKCpD77XbFzFClhQL4w85CXC3UwfIe0rBRmLzFMZ5Y9bph8Dpny/COrQQ==", "dev": true, "requires": { "async": "^3.1.0", "block-stream2": "^2.0.0", "browser-or-node": "^1.3.0", + "buffer-crc32": "^0.2.13", "crypto-browserify": "^3.12.0", "es6-error": "^4.1.1", "fast-xml-parser": "^3.17.5", @@ -37256,13 +37398,12 @@ "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==", - "requires": {} + "integrity": "sha512-U/kMSFtV/bNVgY01FuiGWpRkaQVHozBq5CEBZltFvPt4FcV111hEWkgwqVg9GPPZSEuEdV438PEz8mk8mKpYlA==" }, "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", + "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==" }, "moment-locales-webpack-plugin": { "version": "1.2.0", @@ -37539,9 +37680,9 @@ } }, "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.3.tgz", + "integrity": "sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==" }, "normalize-package-data": { "version": "2.5.0", @@ -38412,12 +38553,12 @@ } }, "polished": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/polished/-/polished-4.1.4.tgz", - "integrity": "sha512-Nq5Mbza+Auo7N3sQb1QMFaQiDO+4UexWuSGR7Cjb4Sw11SZIJcrrFtiZ+L0jT9MBsUsxDboHVASbCLbE1rnECg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.2.2.tgz", + "integrity": "sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==", "dev": true, "requires": { - "@babel/runtime": "^7.16.7" + "@babel/runtime": "^7.17.8" } }, "popper.js": { @@ -38437,9 +38578,9 @@ }, "dependencies": { "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, "requires": { "lodash": "^4.17.14" @@ -38542,29 +38683,25 @@ "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, - "requires": {} + "dev": true }, "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, - "requires": {} + "dev": true }, "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, - "requires": {} + "dev": true }, "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, - "requires": {} + "dev": true }, "postcss-flexbugs-fixes": { "version": "4.2.1", @@ -38808,9 +38945,9 @@ } }, "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==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.4.tgz", + "integrity": "sha512-hbqRRqYfmXoGpzYKeW0/NCZhvNyQIlQeWVSao5iKWdyx7skLvCfQFGIUsP9NUs3dSbPac2IC4Go85/zG+7MlmA==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0", @@ -39238,8 +39375,7 @@ "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, - "requires": {} + "dev": true }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -39431,9 +39567,9 @@ } }, "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==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", "requires": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -39507,15 +39643,15 @@ } }, "css-what": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.0.1.tgz", - "integrity": "sha512-z93ZGFLNc6yaoXAmVhqoSIb+BduplteCt1fepvwhBUQK6MNE4g6fgjpuZKJKp0esUe+vXWlIkwZZjNWoOKw0ZA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true }, "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, "requires": { "domelementtype": "^2.0.1", @@ -39524,9 +39660,9 @@ } }, "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true }, "domhandler": { @@ -39635,9 +39771,9 @@ "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" }, "prismjs": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", - "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.28.0.tgz", + "integrity": "sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw==", "dev": true }, "process": { @@ -40088,27 +40224,23 @@ "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==", - "requires": {} + "integrity": "sha512-OfX0ThSxRYEVGaQSt0DlXfyl5w4DbXHsXetyeivmoQrh9xA9bzVPHNf8aAhOIiwiaxX2WYWpLDB3gcpsDJ9oww==" }, "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==", - "requires": {} + "integrity": "sha512-Cksbgbviuf2mJfMyrKmcu7ycK6zX/ukuQO8dvRZdFWqATf5joalhjFc6etnBdGCcPA2LbhIwz+OPnQxLN/j1Fw==" }, "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, - "requires": {} + "dev": true }, "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==", - "requires": {} + "integrity": "sha512-96qA+mbZyBRmh/3Y5aDgrYLwLndbaRjkP3GlXQtPEQbIH0P66xGcHJ7ui6y/MN85AZWq/V3drA1fJOiEcVkAVA==" }, "react-datepicker": { "version": "2.16.0", @@ -40143,23 +40275,23 @@ } }, "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==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-15.1.2.tgz", + "integrity": "sha512-EaSbMD9iFJDY/o48T3c8wn3uWU+2uxfFojhesZN3LhigJoAIvH2iOjxofSA9KbqhAKP6V9P853G6XG8JngKVtA==", "requires": { - "@react-dnd/invariant": "3.0.0", - "@react-dnd/shallowequal": "3.0.0", - "dnd-core": "15.1.1", + "@react-dnd/invariant": "3.0.1", + "@react-dnd/shallowequal": "3.0.1", + "dnd-core": "15.1.2", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" } }, "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==", + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-15.1.3.tgz", + "integrity": "sha512-HH/8nOEmrrcRGHMqJR91FOwhnLlx5SRLXmsQwZT3IPcBjx88WT+0pWC5A4tDOYDdoooh9k+KMPvWfxooR5TcOA==", "requires": { - "dnd-core": "15.1.1" + "dnd-core": "15.1.2" } }, "react-docgen": { @@ -40201,8 +40333,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", - "dev": true, - "requires": {} + "dev": true }, "react-dom": { "version": "16.14.0", @@ -40240,9 +40371,9 @@ } }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-1.3.0.tgz", + "integrity": "sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==", "dev": true, "requires": { "@babel/runtime": "^7.12.5", @@ -40280,8 +40411,7 @@ "react-lazyload": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-lazyload/-/react-lazyload-3.2.0.tgz", - "integrity": "sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw==", - "requires": {} + "integrity": "sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw==" }, "react-lifecycles-compat": { "version": "3.0.4", @@ -40291,8 +40421,7 @@ "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==", - "requires": {} + "integrity": "sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q==" }, "react-popper": { "version": "1.3.11", @@ -40359,14 +40488,13 @@ "dev": true }, "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==", + "version": "6.7.8", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-6.7.8.tgz", + "integrity": "sha512-0FaEcUBAbn+pq3PT5a9hHRebUfuS1SRLGLpIw8LydU7zX429I6XJgKerKAMPsJH0qWAl6o5bVKNqFJqr6tGPYw==", "requires": { - "lodash.debounce": "^4.0.8", - "lodash.throttle": "^4.1.1", - "prop-types": "^15.6.0", - "resize-observer-polyfill": "^1.5.0" + "@types/resize-observer-browser": "^0.1.6", + "lodash": "^4.17.21", + "resize-observer-polyfill": "^1.5.1" } }, "react-router": { @@ -40457,14 +40585,13 @@ } }, "react-smooth": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-1.0.6.tgz", - "integrity": "sha512-B2vL4trGpNSMSOzFiAul9kFAsxTukL9Wyy9EXtkQy3GJr6sZqW9e1nShdVOJ3hRYamPZ94O17r3Q0bjSw3UYtg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.0.tgz", + "integrity": "sha512-wK4dBBR6P21otowgMT9toZk+GngMplGS1O5gk+2WSiHEXIrQgDvhR5IIlT74Vtu//qpTcipkgo21dD7a7AUNxw==", "requires": { - "lodash": "~4.17.4", - "prop-types": "^15.6.0", + "fast-equals": "^2.0.0", "raf": "^3.4.0", - "react-transition-group": "^2.5.0" + "react-transition-group": "2.9.0" }, "dependencies": { "dom-helpers": { @@ -40674,27 +40801,35 @@ } }, "recharts": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-1.8.5.tgz", - "integrity": "sha512-tM9mprJbXVEBxjM7zHsIy6Cc41oO/pVYqyAsOHLxlJrbNBuLs0PHB3iys2M+RqCF0//k8nJtZF6X6swSkWY3tg==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.1.9.tgz", + "integrity": "sha512-VozH5uznUvGqD7n224FGj7cmMAenlS0HPCs+7r2HeeHiQK6un6z0CTZfWVAB860xbcr4m+BN/EGMPZmYWd34Rg==", "requires": { + "@types/d3-interpolate": "^2.0.0", + "@types/d3-scale": "^3.0.0", + "@types/d3-shape": "^2.0.0", "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" + "d3-interpolate": "^2.0.0", + "d3-scale": "^3.0.0", + "d3-shape": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^6.6.3", + "react-smooth": "^2.0.0", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8" }, "dependencies": { - "core-js": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", - "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "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==" } } }, @@ -40707,34 +40842,18 @@ } }, "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=", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", "requires": { - "balanced-match": "^0.4.2", - "math-expression-evaluator": "^1.2.14", - "reduce-function-call": "^1.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" - } - } - }, - "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==", - "requires": { - "balanced-match": "^1.0.0" + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" } }, "redux": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", - "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", "requires": { "@babel/runtime": "^7.9.2" } @@ -40742,14 +40861,12 @@ "redux-immutable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz", - "integrity": "sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=", - "requires": {} + "integrity": "sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=" }, "redux-thunk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", - "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", - "requires": {} + "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" }, "refractor": { "version": "3.6.0", @@ -40760,6 +40877,14 @@ "hastscript": "^6.0.0", "parse-entities": "^2.0.0", "prismjs": "~1.27.0" + }, + "dependencies": { + "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 + } } }, "regenerate": { @@ -40783,9 +40908,9 @@ "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", "dev": true, "requires": { "@babel/runtime": "^7.8.4" @@ -40802,12 +40927,13 @@ } }, "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==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" } }, "regexpu-core": { @@ -41011,15 +41137,15 @@ } }, "css-what": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.0.1.tgz", - "integrity": "sha512-z93ZGFLNc6yaoXAmVhqoSIb+BduplteCt1fepvwhBUQK6MNE4g6fgjpuZKJKp0esUe+vXWlIkwZZjNWoOKw0ZA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true }, "dom-serializer": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", - "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, "requires": { "domelementtype": "^2.0.1", @@ -41028,9 +41154,9 @@ } }, "domelementtype": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", - "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true }, "domhandler": { @@ -42663,22 +42789,6 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, - "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==", - "requires": { - "css-unit-converter": "^1.1.1", - "postcss-value-parser": "^3.3.0" - }, - "dependencies": { - "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==" - } - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -43226,9 +43336,9 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -43313,14 +43423,14 @@ } }, "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==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/typed-css-modules/-/typed-css-modules-0.7.1.tgz", + "integrity": "sha512-WaWHnLa3HBTCuOCDRDB3wfqoH9ouTxdLGQwzVBxV5x+nbPIr5ZkCrCb4yfU4D1bkzNprrTKTU2ETkYVOLGFmVA==", "dev": true, "requires": { "@types/css-modules-loader-core": "^1.1.0", - "camelcase": "^5.3.1", - "chalk": "^2.1.0", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", "chokidar": "^3.4.0", "css-modules-loader-core": "^1.1.0", "glob": "^7.1.2", @@ -43338,11 +43448,15 @@ "color-convert": "^2.0.1" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, "cliui": { "version": "6.0.0", @@ -43365,6 +43479,12 @@ "path-exists": "^4.0.0" } }, + "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 + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -43389,6 +43509,15 @@ "p-limit": "^2.2.0" } }, + "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, + "requires": { + "has-flag": "^4.0.0" + } + }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -43427,6 +43556,14 @@ "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } } } } @@ -43454,9 +43591,9 @@ "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==" }, "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==", + "version": "3.15.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", + "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", "dev": true, "optional": true }, @@ -43787,14 +43924,12 @@ "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==", - "requires": {} + "integrity": "sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw==" }, "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==", - "requires": {} + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" }, "use-latest": { "version": "1.2.0", @@ -43858,14 +43993,14 @@ "dev": true }, "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==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.0.tgz", + "integrity": "sha512-HcvgY/xaRm7isYmyx+lFKA4uQmfUbN0J4M0nNItvzTvH/iQ9kW5j/t4YSR+Ge323/lrgDAWJoF46tzGQHwBHFw==", "dev": true, "requires": { + "@jridgewell/trace-mapping": "^0.3.7", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" + "convert-source-map": "^1.6.0" } }, "validate-npm-package-license": { @@ -45359,8 +45494,7 @@ "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, - "requires": {} + "dev": true }, "webpack-hot-middleware": { "version": "2.25.1", @@ -45526,9 +45660,9 @@ } }, "winston": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.6.0.tgz", - "integrity": "sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz", + "integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==", "dev": true, "requires": { "@dabh/diagnostics": "^2.0.2", @@ -45671,8 +45805,7 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true, - "requires": {} + "dev": true }, "xml": { "version": "1.0.1", @@ -45725,9 +45858,9 @@ "dev": true }, "yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", "dev": true, "requires": { "cliui": "^7.0.2", diff --git a/frontend/package.json b/frontend/package.json index 652d2dee9..b4bd4f031 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -53,7 +53,7 @@ "react-tippy": "^1.4.0", "react-toastify": "^5.5.0", "react-virtualized": "^9.22.2", - "recharts": "^1.8.5", + "recharts": "^2.1.9", "redux": "^4.0.5", "redux-immutable": "^4.0.0", "redux-thunk": "^2.3.0", diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 16cba8159..63fb417f5 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -3,7 +3,8 @@ "target": "es5", "module": "es2020", "moduleResolution": "node", //? - //"allowJs": true, + // "allowJs": true, + "declaration": true, "allowSyntheticDefaultImports": true, "downlevelIteration": true, //"sourceMap": false, From 9a35dcbc3e4b3e227ead00c37d1ffbb4049ecd87 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Thu, 21 Apr 2022 18:49:09 +0200 Subject: [PATCH 261/294] feat(ui) - toggle annotation manually --- .../AssistActions/AassistActions.css | 7 +++++ .../AssistActions/AssistActions.tsx | 26 ++++++++++++++++--- .../components/Session_/playerBlockHeader.css | 2 +- .../managers/AssistManager.ts | 6 ++--- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/frontend/app/components/Assist/components/AssistActions/AassistActions.css b/frontend/app/components/Assist/components/AssistActions/AassistActions.css index 8a5758d90..77d9b7c81 100644 --- a/frontend/app/components/Assist/components/AssistActions/AassistActions.css +++ b/frontend/app/components/Assist/components/AssistActions/AassistActions.css @@ -2,3 +2,10 @@ opacity: 0.5; pointer-events: none; } + +.divider { + width: 1px; + height: 49px; + margin: 0 10px; + background-color: $gray-light; +} \ No newline at end of file diff --git a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx index 58eadd472..75a770b51 100644 --- a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx +++ b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx @@ -5,7 +5,7 @@ import cn from 'classnames' import { toggleChatWindow } from 'Duck/sessions'; import { connectPlayer } from 'Player/store'; import ChatWindow from '../../ChatWindow'; -import { callPeer, requestReleaseRemoteControl } from 'Player' +import { callPeer, requestReleaseRemoteControl, toggleAnnotation } from 'Player' import { CallingState, ConnectionStatus, RemoteControlStatus } from 'Player/MessageDistributor/managers/AssistManager'; import RequestLocalStream from 'Player/MessageDistributor/managers/LocalStream'; import type { LocalStream } from 'Player/MessageDistributor/managers/LocalStream'; @@ -31,13 +31,14 @@ interface Props { userId: String, toggleChatWindow: (state) => void, calling: CallingState, + annotating: boolean, peerConnectionStatus: ConnectionStatus, remoteControlStatus: RemoteControlStatus, hasPermission: boolean, isEnterprise: boolean, } -function AssistActions({ toggleChatWindow, userId, calling, peerConnectionStatus, remoteControlStatus, hasPermission, isEnterprise }: Props) { +function AssistActions({ toggleChatWindow, userId, calling, annotating, peerConnectionStatus, remoteControlStatus, hasPermission, isEnterprise }: Props) { const [ incomeStream, setIncomeStream ] = useState(null); const [ localStream, setLocalStream ] = useState(null); const [ callObject, setCallObject ] = useState<{ end: ()=>void } | null >(null); @@ -81,6 +82,23 @@ function AssistActions({ toggleChatWindow, userId, calling, peerConnectionStatus return (
+ {onCall && ( + <> +
+ +
+
+ + )}
- +
+
{ export default con(connectPlayer(state => ({ calling: state.calling, + annotating: state.annotating, remoteControlStatus: state.remoteControl, peerConnectionStatus: state.peerConnectionStatus, }))(AssistActions)) diff --git a/frontend/app/components/Session_/playerBlockHeader.css b/frontend/app/components/Session_/playerBlockHeader.css index 3aa858053..bec261a1d 100644 --- a/frontend/app/components/Session_/playerBlockHeader.css +++ b/frontend/app/components/Session_/playerBlockHeader.css @@ -8,7 +8,7 @@ .divider { width: 1px; height: 49px; - margin: 0 15px; + margin: 0 10px; background-color: $gray-light; } diff --git a/frontend/app/player/MessageDistributor/managers/AssistManager.ts b/frontend/app/player/MessageDistributor/managers/AssistManager.ts index 892054cc4..0cd752f1d 100644 --- a/frontend/app/player/MessageDistributor/managers/AssistManager.ts +++ b/frontend/app/player/MessageDistributor/managers/AssistManager.ts @@ -323,7 +323,7 @@ export default class AssistManager { private handleCallEnd() { this.callArgs && this.callArgs.onCallEnd() this.callConnection && this.callConnection.close() - update({ calling: CallingState.NoCall }) + update({ calling: CallingState.NoCall, annotating: false }) this.callArgs = null this.annot?.remove() this.annot = null @@ -377,7 +377,7 @@ export default class AssistManager { if (typeof enable !== "boolean") { enable = !!getState().annotating } - if (enable && !this.annot) { + if (!enable && !this.annot) { const annot = this.annot = new AnnotationCanvas() annot.mount(this.md.overlay) annot.canvas.addEventListener("mousedown", e => { @@ -404,7 +404,7 @@ export default class AssistManager { this.socket.emit("moveAnnotation", [ data.x, data.y ]) }) update({ annotating: true }) - } else if (!enable && !!this.annot) { + } else if (enable && !!this.annot) { this.annot.remove() this.annot = null update({ annotating: false }) From 77e7ad4edb941a374a7eb3fd2d93885373eeca07 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Thu, 21 Apr 2022 20:30:08 +0200 Subject: [PATCH 262/294] fix(tracker):3.5.9:fixed webwoeker refactoring --- tracker/tracker/package.json | 2 +- tracker/tracker/src/main/modules/mouse.ts | 4 ---- tracker/tracker/src/webworker/BatchWriter.ts | 10 ++++---- tracker/tracker/src/webworker/index.ts | 24 ++++++++++++++------ 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index e5fdefddf..0c5b06634 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "3.5.7", + "version": "3.5.9", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/src/main/modules/mouse.ts b/tracker/tracker/src/main/modules/mouse.ts index 196a89653..b72e5dbb9 100644 --- a/tracker/tracker/src/main/modules/mouse.ts +++ b/tracker/tracker/src/main/modules/mouse.ts @@ -83,10 +83,6 @@ function _getTarget(target: Element): Element | null { } export default function (app: App): void { - // const options: Options = Object.assign( - // {}, - // opts, - // ); function getTargetLabel(target: Element): string { const dl = getLabelAttribute(target); diff --git a/tracker/tracker/src/webworker/BatchWriter.ts b/tracker/tracker/src/webworker/BatchWriter.ts index de0e956ef..5f220bbec 100644 --- a/tracker/tracker/src/webworker/BatchWriter.ts +++ b/tracker/tracker/src/webworker/BatchWriter.ts @@ -28,9 +28,10 @@ export default class BatchWriter { this.beaconSizeLimit = limit } + // TODO: clear workflow writeMessage(message: Message) { if (message instanceof Timestamp) { - this.timestamp = (message).timestamp; + this.timestamp = (message).timestamp } if (!message.encode(this.writer)) { @@ -58,10 +59,11 @@ export default class BatchWriter { this.isEmpty = false } - flush(): Uint8Array | null { - if (this.isEmpty) { return null } + finaliseBatch() { + if (this.isEmpty) { return } + this.onBatch(this.writer.flush()) + this.prepareBatchMeta() this.isEmpty = true - return this.writer.flush() } clean() { diff --git a/tracker/tracker/src/webworker/index.ts b/tracker/tracker/src/webworker/index.ts index 519deeb0f..b9640e9e2 100644 --- a/tracker/tracker/src/webworker/index.ts +++ b/tracker/tracker/src/webworker/index.ts @@ -2,6 +2,7 @@ import Message from "../messages/message.js"; import { classes, SetPageVisibility, + MouseMove, } from "../messages/index.js"; import QueueSender from "./QueueSender.js"; import BatchWriter from "./BatchWriter.js"; @@ -15,11 +16,10 @@ let sender: QueueSender | null = null let writer: BatchWriter | null = null function send(): void { - if (!sender || !writer) { + if (!writer) { return } - const batch = writer.flush() - batch && sender.push(batch) + writer.finaliseBatch() } @@ -58,6 +58,10 @@ self.onmessage = ({ data }: MessageEvent) => { } if (Array.isArray(data)) { + if (!writer) { + throw new Error("WebWorker: writer not initialised.") + } + const w = writer // Message[] data.forEach((data) => { const message: Message = new (classes.get(data._id))(); @@ -68,8 +72,8 @@ self.onmessage = ({ data }: MessageEvent) => { } else { clearTimeout(restartTimeoutID) } - } - writer && writer.writeMessage(message) + } + w.writeMessage(message) }) return } @@ -100,8 +104,14 @@ self.onmessage = ({ data }: MessageEvent) => { } if (data.type === "auth") { - sender && sender.authorise(data.token) - data.beaconSizeLimit && writer && writer.setBeaconSizeLimit(data.beaconSizeLimit) + if (!sender) { + throw new Error("WebWorker: sender not initialised. Recieved auth.") + } + if (!writer) { + throw new Error("WebWorker: writer not initialised. Recieved auth.") + } + sender.authorise(data.token) + data.beaconSizeLimit && writer.setBeaconSizeLimit(data.beaconSizeLimit) return } }; From d472e2094891b0e072c2cb0cab1c38d2f7b6619c Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Thu, 21 Apr 2022 20:59:59 +0200 Subject: [PATCH 263/294] feat(api): requests change base_path to path feat(db): requests change base_path to path --- api/chalicelib/core/events.py | 2 +- .../db/init_dbs/postgresql/init_schema.sql | 94 +++++++++---------- .../db/init_dbs/postgresql/1.6.0/1.6.0.sql | 6 +- .../db/init_dbs/postgresql/init_schema.sql | 6 +- 4 files changed, 51 insertions(+), 57 deletions(-) diff --git a/api/chalicelib/core/events.py b/api/chalicelib/core/events.py index 3e39ba4bd..81378a49c 100644 --- a/api/chalicelib/core/events.py +++ b/api/chalicelib/core/events.py @@ -341,7 +341,7 @@ class event_type: INPUT = Event(ui_type=schemas.EventType.input, table="events.inputs", column="label") LOCATION = Event(ui_type=schemas.EventType.location, table="events.pages", column="base_path") CUSTOM = Event(ui_type=schemas.EventType.custom, table="events_common.customs", column="name") - REQUEST = Event(ui_type=schemas.EventType.request, table="events_common.requests", column="base_path") + REQUEST = Event(ui_type=schemas.EventType.request, table="events_common.requests", column="path") GRAPHQL = Event(ui_type=schemas.EventType.graphql, table="events.graphql", column="name") STATEACTION = Event(ui_type=schemas.EventType.state_action, table="events.state_actions", column="name") ERROR = Event(ui_type=schemas.EventType.error, table="events.errors", 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 f01ad9001..0b1692b20 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -100,38 +100,36 @@ $$ LANGUAGE plpgsql; DO $$ BEGIN - IF (with to_check (name) as ( - values ('alerts'), - ('announcements'), - ('assigned_sessions'), - ('autocomplete'), - ('basic_authentication'), - ('dashboards'), - ('dashboard_widgets'), - ('errors'), - ('funnels'), - ('integrations'), - ('issues'), - ('jira_cloud'), - ('jobs'), - ('metric_series'), - ('metrics'), - ('notifications'), - ('oauth_authentication'), - ('projects'), - ('roles'), - ('roles_projects'), - ('searches'), - ('sessions'), - ('tenants'), - ('traces'), - ('user_favorite_errors'), - ('user_favorite_sessions'), - ('user_viewed_errors'), - ('user_viewed_sessions'), - ('users'), - ('webhooks') - ) + IF (with to_check (name) as (values ('alerts'), + ('announcements'), + ('assigned_sessions'), + ('autocomplete'), + ('basic_authentication'), + ('dashboards'), + ('dashboard_widgets'), + ('errors'), + ('funnels'), + ('integrations'), + ('issues'), + ('jira_cloud'), + ('jobs'), + ('metric_series'), + ('metrics'), + ('notifications'), + ('oauth_authentication'), + ('projects'), + ('roles'), + ('roles_projects'), + ('searches'), + ('sessions'), + ('tenants'), + ('traces'), + ('user_favorite_errors'), + ('user_favorite_sessions'), + ('user_viewed_errors'), + ('user_viewed_sessions'), + ('users'), + ('webhooks')) select bool_and(exists(select * from information_schema.tables t where table_schema = 'public' @@ -914,16 +912,14 @@ LANGUAGE plpgsql; DO $$ BEGIN - IF (with to_check (name) as ( - values ('clicks'), - ('errors'), - ('graphql'), - ('inputs'), - ('pages'), - ('performance'), - ('resources'), - ('state_actions') - ) + IF (with to_check (name) as (values ('clicks'), + ('errors'), + ('graphql'), + ('inputs'), + ('pages'), + ('performance'), + ('resources'), + ('state_actions')) select bool_and(exists(select * from information_schema.tables t where table_schema = 'events' @@ -1176,11 +1172,9 @@ LANGUAGE plpgsql; DO $$ BEGIN - IF (with to_check (name) as ( - values ('customs'), - ('issues'), - ('requests') - ) + IF (with to_check (name) as (values ('customs'), + ('issues'), + ('requests')) select bool_and(exists(select * from information_schema.tables t where table_schema = 'events_common' @@ -1238,7 +1232,7 @@ $$ status_code smallint NULL, method http_method NULL, host text NULL, - base_path text NULL, + path text NULL, query text NULL, PRIMARY KEY (session_id, timestamp, seq_index) ); @@ -1263,8 +1257,8 @@ $$ CREATE INDEX IF NOT EXISTS requests_status_code_nn_idx ON events_common.requests (status_code) WHERE status_code IS NOT NULL; CREATE INDEX IF NOT EXISTS requests_host_nn_idx ON events_common.requests (host) WHERE host IS NOT NULL; CREATE INDEX IF NOT EXISTS requests_host_nn_gin_idx ON events_common.requests USING GIN (host gin_trgm_ops) WHERE host IS NOT NULL; - CREATE INDEX IF NOT EXISTS requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; - CREATE INDEX IF NOT EXISTS requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; + CREATE INDEX IF NOT EXISTS requests_path_nn_idx ON events_common.requests (path) WHERE path IS NOT NULL; + CREATE INDEX IF NOT EXISTS requests_path_nn_gin_idx ON events_common.requests USING GIN (path gin_trgm_ops) WHERE path IS NOT NULL; CREATE INDEX IF NOT EXISTS requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; CREATE INDEX IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; diff --git a/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql b/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql index dbfdb6d85..1c5e0a06f 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql @@ -54,7 +54,7 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets ALTER TABLE events_common.requests ADD COLUMN IF NOT EXISTS host text NULL, - ADD COLUMN IF NOT EXISTS base_path text NULL, + ADD COLUMN IF NOT EXISTS path text NULL, ADD COLUMN IF NOT EXISTS query text NULL; ALTER TABLE events.pages @@ -312,8 +312,8 @@ ON CONFLICT (predefined_key) DO UPDATE CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_idx ON events_common.requests (host) WHERE host IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_gin_idx ON events_common.requests USING GIN (host gin_trgm_ops) WHERE host IS NOT NULL; -CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; -CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_path_nn_idx ON events_common.requests (path) WHERE path IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_path_nn_gin_idx ON events_common.requests USING GIN (path gin_trgm_ops) WHERE path IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 5e0323b9c..da2c7bc9c 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -646,7 +646,7 @@ $$ status_code smallint NULL, method http_method NULL, host text NULL, - base_path text NULL, + path text NULL, query text NULL, PRIMARY KEY (session_id, timestamp, seq_index) ); @@ -669,8 +669,8 @@ $$ CREATE INDEX requests_status_code_nn_idx ON events_common.requests (status_code) WHERE status_code IS NOT NULL; CREATE INDEX requests_host_nn_idx ON events_common.requests (host) WHERE host IS NOT NULL; CREATE INDEX requests_host_nn_gin_idx ON events_common.requests USING GIN (host gin_trgm_ops) WHERE host IS NOT NULL; - CREATE INDEX requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; - CREATE INDEX requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; + CREATE INDEX requests_path_nn_idx ON events_common.requests (path) WHERE path IS NOT NULL; + CREATE INDEX requests_path_nn_gin_idx ON events_common.requests USING GIN (path gin_trgm_ops) WHERE path IS NOT NULL; CREATE INDEX requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; CREATE INDEX requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; From 07168194921e7e94885874d9b84667ca0b803513 Mon Sep 17 00:00:00 2001 From: ShiKhu Date: Fri, 22 Apr 2022 00:29:25 +0200 Subject: [PATCH 264/294] fix(backend): url path correct parsing --- backend/pkg/db/postgres/messages-web.go | 2 +- backend/pkg/url/url.go | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/pkg/db/postgres/messages-web.go b/backend/pkg/db/postgres/messages-web.go index e540dc6a0..197924fa9 100644 --- a/backend/pkg/db/postgres/messages-web.go +++ b/backend/pkg/db/postgres/messages-web.go @@ -85,7 +85,7 @@ func (conn *Conn) InsertWebPageEvent(sessionID uint64, e *PageEvent) error { host, path, query, e.DomContentLoadedEventEnd, e.LoadEventEnd, e.ResponseEnd, e.FirstPaint, e.FirstContentfulPaint, e.SpeedIndex, e.VisuallyComplete, e.TimeToInteractive, - calcResponseTime(e), calcDomBuildingTime(e) + calcResponseTime(e), calcDomBuildingTime(e), ); err != nil { return err } diff --git a/backend/pkg/url/url.go b/backend/pkg/url/url.go index 48cd0ef8d..0ac0f9e08 100644 --- a/backend/pkg/url/url.go +++ b/backend/pkg/url/url.go @@ -14,6 +14,10 @@ func GetURLParts(rawURL string) (string, string, string, error) { if err != nil { return "", "", "", err } - // u.Scheme ? - return u.Host, u.RawPath, u.RawQuery, nil + // u.Scheme u.Fragment / RawFragment ? + path := u.Path + if u.RawPath != "" { + path = u.RawPath + } + return u.Host, path, u.RawQuery, nil } From 05fb3abf2e06f5758dbbe03a0e9e526dff2b3b81 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 22 Apr 2022 11:17:09 +0200 Subject: [PATCH 265/294] change(ui) - icon changes for assist --- .../Assist/components/AssistActions/AssistActions.tsx | 4 ++-- frontend/app/svg/icons/pencil-stop.svg | 11 +++++++++++ frontend/app/svg/icons/window-x.svg | 5 +++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 frontend/app/svg/icons/pencil-stop.svg create mode 100644 frontend/app/svg/icons/window-x.svg diff --git a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx index 75a770b51..9584188fc 100644 --- a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx +++ b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx @@ -94,7 +94,7 @@ function AssistActions({ toggleChatWindow, userId, calling, annotating, peerConn onClick={ toggleAnnotation } role="button" > - +
@@ -109,7 +109,7 @@ function AssistActions({ toggleChatWindow, userId, calling, annotating, peerConn onClick={ requestReleaseRemoteControl } role="button" > - +
diff --git a/frontend/app/svg/icons/pencil-stop.svg b/frontend/app/svg/icons/pencil-stop.svg new file mode 100644 index 000000000..f7c192810 --- /dev/null +++ b/frontend/app/svg/icons/pencil-stop.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/app/svg/icons/window-x.svg b/frontend/app/svg/icons/window-x.svg new file mode 100644 index 000000000..facc1178f --- /dev/null +++ b/frontend/app/svg/icons/window-x.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From b5a6d4e89253a5d082171f5625b62257c4783b28 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 22 Apr 2022 12:39:22 +0200 Subject: [PATCH 266/294] change(ui) - npm updates --- frontend/package-lock.json | 101 +++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 37 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8a844720b..b448175da 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -4679,7 +4679,7 @@ "version": "14.18.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.13.tgz", "integrity": "sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA==", - "dev": true + "devOptional": true }, "node_modules/@types/node-fetch": { "version": "2.6.1", @@ -4731,7 +4731,7 @@ "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "devOptional": true }, "node_modules/@types/q": { "version": "1.5.5", @@ -4749,7 +4749,7 @@ "version": "18.0.5", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.5.tgz", "integrity": "sha512-UPxNGInDCIKlfqBrm8LDXYWNfLHwIdisWcsH5GpMyGjhEDLFgTtlRBaoWuCua9HcyuE0rMkmAeZ3FXV1pYLIYQ==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4769,7 +4769,7 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "dev": true + "devOptional": true }, "node_modules/@types/resize-observer-browser": { "version": "0.1.7", @@ -4780,7 +4780,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true + "devOptional": true }, "node_modules/@types/source-list-map": { "version": "0.1.2", @@ -29063,7 +29063,7 @@ "version": "14.18.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.13.tgz", "integrity": "sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA==", - "dev": true + "devOptional": true }, "@types/node-fetch": { "version": "2.6.1", @@ -29115,7 +29115,7 @@ "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "devOptional": true }, "@types/q": { "version": "1.5.5", @@ -29133,7 +29133,7 @@ "version": "18.0.5", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.5.tgz", "integrity": "sha512-UPxNGInDCIKlfqBrm8LDXYWNfLHwIdisWcsH5GpMyGjhEDLFgTtlRBaoWuCua9HcyuE0rMkmAeZ3FXV1pYLIYQ==", - "dev": true, + "devOptional": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -29144,7 +29144,7 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "dev": true + "devOptional": true } } }, @@ -29166,7 +29166,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true + "devOptional": true }, "@types/source-list-map": { "version": "0.1.2", @@ -29531,13 +29531,15 @@ "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 + "dev": true, + "requires": {} }, "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 + "dev": true, + "requires": {} }, "ansi-align": { "version": "3.0.1", @@ -30167,7 +30169,8 @@ "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 + "dev": true, + "requires": {} }, "babel-plugin-polyfill-corejs2": { "version": "0.3.1", @@ -31007,7 +31010,8 @@ "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 + "dev": true, + "requires": {} }, "class-utils": { "version": "0.3.6", @@ -32066,7 +32070,8 @@ "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 + "dev": true, + "requires": {} }, "css-loader": { "version": "3.6.0", @@ -32377,7 +32382,8 @@ "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 + "dev": true, + "requires": {} }, "csso": { "version": "4.2.0", @@ -33186,7 +33192,8 @@ "ws": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "requires": {} } } }, @@ -36941,7 +36948,8 @@ "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 + "dev": true, + "requires": {} }, "md5-file": { "version": "5.0.0", @@ -37398,7 +37406,8 @@ "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==" + "integrity": "sha512-U/kMSFtV/bNVgY01FuiGWpRkaQVHozBq5CEBZltFvPt4FcV111hEWkgwqVg9GPPZSEuEdV438PEz8mk8mKpYlA==", + "requires": {} }, "moment": { "version": "2.29.3", @@ -38683,25 +38692,29 @@ "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 + "dev": true, + "requires": {} }, "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 + "dev": true, + "requires": {} }, "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 + "dev": true, + "requires": {} }, "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 + "dev": true, + "requires": {} }, "postcss-flexbugs-fixes": { "version": "4.2.1", @@ -39375,7 +39388,8 @@ "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 + "dev": true, + "requires": {} }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -40224,23 +40238,27 @@ "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==" + "integrity": "sha512-OfX0ThSxRYEVGaQSt0DlXfyl5w4DbXHsXetyeivmoQrh9xA9bzVPHNf8aAhOIiwiaxX2WYWpLDB3gcpsDJ9oww==", + "requires": {} }, "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==" + "integrity": "sha512-Cksbgbviuf2mJfMyrKmcu7ycK6zX/ukuQO8dvRZdFWqATf5joalhjFc6etnBdGCcPA2LbhIwz+OPnQxLN/j1Fw==", + "requires": {} }, "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 + "dev": true, + "requires": {} }, "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==" + "integrity": "sha512-96qA+mbZyBRmh/3Y5aDgrYLwLndbaRjkP3GlXQtPEQbIH0P66xGcHJ7ui6y/MN85AZWq/V3drA1fJOiEcVkAVA==", + "requires": {} }, "react-datepicker": { "version": "2.16.0", @@ -40333,7 +40351,8 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", - "dev": true + "dev": true, + "requires": {} }, "react-dom": { "version": "16.14.0", @@ -40411,7 +40430,8 @@ "react-lazyload": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-lazyload/-/react-lazyload-3.2.0.tgz", - "integrity": "sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw==" + "integrity": "sha512-zJlrG8QyVZz4+xkYZH5v1w3YaP5wEFaYSUWC4CT9UXfK75IfRAIEdnyIUF+dXr3kX2MOtL1lUaZmaQZqrETwgw==", + "requires": {} }, "react-lifecycles-compat": { "version": "3.0.4", @@ -40421,7 +40441,8 @@ "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==" + "integrity": "sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q==", + "requires": {} }, "react-popper": { "version": "1.3.11", @@ -40861,12 +40882,14 @@ "redux-immutable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz", - "integrity": "sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=" + "integrity": "sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=", + "requires": {} }, "redux-thunk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", - "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" + "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", + "requires": {} }, "refractor": { "version": "3.6.0", @@ -43924,12 +43947,14 @@ "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==" + "integrity": "sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw==", + "requires": {} }, "use-isomorphic-layout-effect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} }, "use-latest": { "version": "1.2.0", @@ -45494,7 +45519,8 @@ "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 + "dev": true, + "requires": {} }, "webpack-hot-middleware": { "version": "2.25.1", @@ -45805,7 +45831,8 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true + "dev": true, + "requires": {} }, "xml": { "version": "1.0.1", From daa78c3391bcd67ce97e2ca283a4181d4dd63316 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 22 Apr 2022 12:44:18 +0200 Subject: [PATCH 267/294] change(ui) - npm updates --- frontend/package-lock.json | 144 ++++++++++++++++++------------------- frontend/package.json | 2 +- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b448175da..75fcde026 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,7 +21,7 @@ "luxon": "^1.24.1", "mobx": "^6.3.8", "mobx-react-lite": "^3.1.6", - "moment": "^2.27.0", + "moment": "^2.29.2", "moment-range": "^4.0.2", "peerjs": "^1.3.2", "rc-time-picker": "^3.7.3", @@ -4293,9 +4293,9 @@ } }, "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==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/@storybook/router": { @@ -4679,7 +4679,7 @@ "version": "14.18.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.13.tgz", "integrity": "sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA==", - "devOptional": true + "dev": true }, "node_modules/@types/node-fetch": { "version": "2.6.1", @@ -4731,7 +4731,7 @@ "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "devOptional": true + "dev": true }, "node_modules/@types/q": { "version": "1.5.5", @@ -4746,10 +4746,10 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.0.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.5.tgz", - "integrity": "sha512-UPxNGInDCIKlfqBrm8LDXYWNfLHwIdisWcsH5GpMyGjhEDLFgTtlRBaoWuCua9HcyuE0rMkmAeZ3FXV1pYLIYQ==", - "devOptional": true, + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.6.tgz", + "integrity": "sha512-bPqwzJRzKtfI0mVYr5R+1o9BOE8UEXefwc1LwcBtfnaAn6OoqMhLa/91VA8aeWfDPJt1kHvYKI8RHcQybZLHHA==", + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4769,7 +4769,7 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "devOptional": true + "dev": true }, "node_modules/@types/resize-observer-browser": { "version": "0.1.7", @@ -4780,7 +4780,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "devOptional": true + "dev": true }, "node_modules/@types/source-list-map": { "version": "0.1.2", @@ -5583,9 +5583,9 @@ "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==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/async": { @@ -5700,9 +5700,9 @@ } }, "node_modules/aws-sdk": { - "version": "2.1118.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1118.0.tgz", - "integrity": "sha512-R3g06c4RC0Gz/lwMA7wgC7+FwYf5vaO30sPIigoX5m6Tfb7tdzfCYD7pnpvkPRNUvWJ3f5kQk+pEeW25DstRrQ==", + "version": "2.1119.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1119.0.tgz", + "integrity": "sha512-f9xoIWo0/kLVfQ65UCkQ7G+Oxl/mjmdGQNPDcylU8dOjXGfc4cgWxTLThRLCsf9l/4yUjWgo+KtJgY6BWc9UjA==", "dev": true, "dependencies": { "buffer": "4.9.2", @@ -9424,9 +9424,9 @@ } }, "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==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/dotenv": { @@ -9461,9 +9461,9 @@ } }, "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==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/duplexer": { @@ -9525,9 +9525,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.117", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.117.tgz", - "integrity": "sha512-ypZHxY+Sf/PXu7LVN+xoeanyisnJeSOy8Ki439L/oLueZb4c72FI45zXcK3gPpmTwyufh9m6NnbMLXnJh/0Fxg==" + "version": "1.4.118", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz", + "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==" }, "node_modules/element-resize-detector": { "version": "1.2.4", @@ -12602,9 +12602,9 @@ "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==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/html-minifier/node_modules/commander": { @@ -16160,9 +16160,9 @@ } }, "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==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true }, "node_modules/pascalcase": { @@ -23219,11 +23219,11 @@ } }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/use-isomorphic-layout-effect": { @@ -28725,9 +28725,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true } } @@ -29063,7 +29063,7 @@ "version": "14.18.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.13.tgz", "integrity": "sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA==", - "devOptional": true + "dev": true }, "@types/node-fetch": { "version": "2.6.1", @@ -29115,7 +29115,7 @@ "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "devOptional": true + "dev": true }, "@types/q": { "version": "1.5.5", @@ -29130,10 +29130,10 @@ "dev": true }, "@types/react": { - "version": "18.0.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.5.tgz", - "integrity": "sha512-UPxNGInDCIKlfqBrm8LDXYWNfLHwIdisWcsH5GpMyGjhEDLFgTtlRBaoWuCua9HcyuE0rMkmAeZ3FXV1pYLIYQ==", - "devOptional": true, + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.6.tgz", + "integrity": "sha512-bPqwzJRzKtfI0mVYr5R+1o9BOE8UEXefwc1LwcBtfnaAn6OoqMhLa/91VA8aeWfDPJt1kHvYKI8RHcQybZLHHA==", + "dev": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -29144,7 +29144,7 @@ "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", - "devOptional": true + "dev": true } } }, @@ -29166,7 +29166,7 @@ "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "devOptional": true + "dev": true }, "@types/source-list-map": { "version": "0.1.2", @@ -29845,9 +29845,9 @@ }, "dependencies": { "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true } } @@ -29944,9 +29944,9 @@ "dev": true }, "aws-sdk": { - "version": "2.1118.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1118.0.tgz", - "integrity": "sha512-R3g06c4RC0Gz/lwMA7wgC7+FwYf5vaO30sPIigoX5m6Tfb7tdzfCYD7pnpvkPRNUvWJ3f5kQk+pEeW25DstRrQ==", + "version": "2.1119.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1119.0.tgz", + "integrity": "sha512-f9xoIWo0/kLVfQ65UCkQ7G+Oxl/mjmdGQNPDcylU8dOjXGfc4cgWxTLThRLCsf9l/4yUjWgo+KtJgY6BWc9UjA==", "dev": true, "requires": { "buffer": "4.9.2", @@ -32985,9 +32985,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true } } @@ -33018,9 +33018,9 @@ }, "dependencies": { "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true } } @@ -33082,9 +33082,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.117", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.117.tgz", - "integrity": "sha512-ypZHxY+Sf/PXu7LVN+xoeanyisnJeSOy8Ki439L/oLueZb4c72FI45zXcK3gPpmTwyufh9m6NnbMLXnJh/0Fxg==" + "version": "1.4.118", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.118.tgz", + "integrity": "sha512-maZIKjnYDvF7Fs35nvVcyr44UcKNwybr93Oba2n3HkKDFAtk0svERkLN/HyczJDS3Fo4wU9th9fUQd09ZLtj1w==" }, "element-resize-detector": { "version": "1.2.4", @@ -35573,9 +35573,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true } } @@ -38339,9 +38339,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true } } @@ -43945,9 +43945,9 @@ "dev": true }, "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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", "requires": {} }, "use-isomorphic-layout-effect": { diff --git a/frontend/package.json b/frontend/package.json index b4bd4f031..7f51ba174 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,7 +28,7 @@ "luxon": "^1.24.1", "mobx": "^6.3.8", "mobx-react-lite": "^3.1.6", - "moment": "^2.27.0", + "moment": "^2.29.2", "moment-range": "^4.0.2", "peerjs": "^1.3.2", "rc-time-picker": "^3.7.3", From 98910571053411724c5766bcc5077f4308e9d77d Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Fri, 22 Apr 2022 12:45:03 +0200 Subject: [PATCH 268/294] feat(api): pages change base_path to path feat(db): pages change base_path to path --- api/chalicelib/core/events.py | 2 +- api/chalicelib/core/insights.py | 2 +- api/chalicelib/core/sessions.py | 4 +- .../db/init_dbs/postgresql/1.6.0/1.6.0.sql | 39 ++++++++++++++++--- .../db/init_dbs/postgresql/init_schema.sql | 9 +---- .../db/init_dbs/postgresql/1.6.0/1.6.0.sql | 33 ++++++++++++++-- .../db/init_dbs/postgresql/init_schema.sql | 15 ++----- 7 files changed, 73 insertions(+), 31 deletions(-) diff --git a/api/chalicelib/core/events.py b/api/chalicelib/core/events.py index 81378a49c..272b86002 100644 --- a/api/chalicelib/core/events.py +++ b/api/chalicelib/core/events.py @@ -339,7 +339,7 @@ def __generic_autocomplete(event: Event): class event_type: CLICK = Event(ui_type=schemas.EventType.click, table="events.clicks", column="label") INPUT = Event(ui_type=schemas.EventType.input, table="events.inputs", column="label") - LOCATION = Event(ui_type=schemas.EventType.location, table="events.pages", column="base_path") + LOCATION = Event(ui_type=schemas.EventType.location, table="events.pages", column="path") CUSTOM = Event(ui_type=schemas.EventType.custom, table="events_common.customs", column="name") REQUEST = Event(ui_type=schemas.EventType.request, table="events_common.requests", column="path") GRAPHQL = Event(ui_type=schemas.EventType.graphql, table="events.graphql", column="name") diff --git a/api/chalicelib/core/insights.py b/api/chalicelib/core/insights.py index e5219344b..c04fd3981 100644 --- a/api/chalicelib/core/insights.py +++ b/api/chalicelib/core/insights.py @@ -21,7 +21,7 @@ def __transform_journey(rows): JOURNEY_DEPTH = 5 JOURNEY_TYPES = { - "PAGES": {"table": "events.pages", "column": "base_path", "table_id": "message_id"}, + "PAGES": {"table": "events.pages", "column": "path", "table_id": "message_id"}, "CLICK": {"table": "events.clicks", "column": "label", "table_id": "message_id"}, # "VIEW": {"table": "events_ios.views", "column": "name", "table_id": "seq_index"}, TODO: enable this for SAAS only "EVENT": {"table": "events_common.customs", "column": "name", "table_id": "seq_index"} diff --git a/api/chalicelib/core/sessions.py b/api/chalicelib/core/sessions.py index 380ed2dd2..adc549d1e 100644 --- a/api/chalicelib/core/sessions.py +++ b/api/chalicelib/core/sessions.py @@ -353,8 +353,8 @@ def search2_series(data: schemas.SessionsSearchPayloadSchema, project_id: int, d full_args[arg_name] = metric_value[i] extra_where = f"WHERE ({' OR '.join(extra_where)})" elif metric_of == schemas.TableMetricOfType.visited_url: - main_col = "base_path" - extra_col = ", base_path" + main_col = "path" + extra_col = ", path" main_query = cur.mogrify(f"""{pre_query} SELECT COUNT(*) AS count, COALESCE(JSONB_AGG(users_sessions) FILTER ( WHERE rn <= 200 ), '[]'::JSONB) AS values FROM (SELECT {main_col} AS name, diff --git a/ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql b/ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql index 7bb8e6b88..d7eeff911 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql @@ -53,12 +53,36 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets ); ALTER TABLE events_common.requests - ADD COLUMN IF NOT EXISTS host text NULL, - ADD COLUMN IF NOT EXISTS base_path text NULL, - ADD COLUMN IF NOT EXISTS query text NULL; + ADD COLUMN IF NOT EXISTS host text NULL, + ADD COLUMN IF NOT EXISTS path text NULL, + ADD COLUMN IF NOT EXISTS query text NULL; ALTER TABLE events.pages ADD COLUMN IF NOT EXISTS query text NULL; + +DO +$$ + BEGIN + IF EXISTS(SELECT * + FROM information_schema.columns + WHERE table_schema = 'events' + AND table_name = 'pages' + AND column_name = 'base_path') + THEN + ALTER TABLE events.pages + DROP COLUMN IF EXISTS path; + ALTER TABLE events.pages + RENAME COLUMN base_path TO path; + DROP INDEX IF EXISTS events.pages_base_path_gin_idx2; + DROP INDEX IF EXISTS pages_base_path_idx2; + ALTER INDEX IF EXISTS events.pages_base_path_gin_idx RENAME TO pages_path_gin_idx; + ALTER INDEX IF EXISTS events.pages_base_path_idx RENAME TO pages_path_idx; + ALTER INDEX IF EXISTS events.pages_base_path_session_id_timestamp_idx RENAME TO pages_path_session_id_timestamp_idx; + ALTER INDEX IF EXISTS events.pages_base_path_base_pathLNGT2_idx RENAME TO pages_path_pathLNGT2_idx; + END IF; + END +$$; + COMMIT; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; @@ -312,10 +336,13 @@ ON CONFLICT (predefined_key) DO UPDATE CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_idx ON events_common.requests (host) WHERE host IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_host_nn_gin_idx ON events_common.requests USING GIN (host gin_trgm_ops) WHERE host IS NOT NULL; -CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_idx ON events_common.requests (base_path) WHERE base_path IS NOT NULL; -CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_base_path_nn_gin_idx ON events_common.requests USING GIN (base_path gin_trgm_ops) WHERE base_path IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_path_nn_idx ON events_common.requests (path) WHERE path IS NOT NULL; +CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_path_nn_gin_idx ON events_common.requests USING GIN (path gin_trgm_ops) WHERE path IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_idx ON events_common.requests (query) WHERE query IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; -CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; \ No newline at end of file +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; + +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_path_session_id_timestamp_idx ON events.pages (path, session_id, timestamp); +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_path_pathLNGT2_idx ON events.pages (path) WHERE length(path) > 2; \ 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 0b1692b20..461a414fc 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -934,7 +934,6 @@ $$ timestamp bigint NOT NULL, host text NOT NULL, path text NOT NULL, - base_path text NOT NULL, query text NULL, referrer text DEFAULT NULL, base_referrer text DEFAULT NULL, @@ -952,13 +951,9 @@ $$ PRIMARY KEY (session_id, message_id) ); CREATE INDEX IF NOT EXISTS pages_session_id_idx ON events.pages (session_id); - CREATE INDEX IF NOT EXISTS pages_base_path_gin_idx ON events.pages USING GIN (base_path gin_trgm_ops); CREATE INDEX IF NOT EXISTS pages_base_referrer_gin_idx ON events.pages USING GIN (base_referrer gin_trgm_ops); CREATE INDEX IF NOT EXISTS pages_timestamp_idx ON events.pages (timestamp); CREATE INDEX IF NOT EXISTS pages_session_id_timestamp_idx ON events.pages (session_id, timestamp); - CREATE INDEX IF NOT EXISTS pages_base_path_gin_idx2 ON events.pages USING GIN (RIGHT(base_path, length(base_path) - 1) gin_trgm_ops); - CREATE INDEX IF NOT EXISTS pages_base_path_idx ON events.pages (base_path); - CREATE INDEX IF NOT EXISTS pages_base_path_idx2 ON events.pages (RIGHT(base_path, length(base_path) - 1)); CREATE INDEX IF NOT EXISTS pages_base_referrer_idx ON events.pages (base_referrer); CREATE INDEX IF NOT EXISTS pages_base_referrer_gin_idx2 ON events.pages USING GIN (RIGHT(base_referrer, length(base_referrer) - @@ -993,8 +988,8 @@ $$ 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); - CREATE INDEX IF NOT EXISTS pages_base_path_base_pathLNGT2_idx ON events.pages (base_path) WHERE length(base_path) > 2; + CREATE INDEX IF NOT EXISTS pages_path_session_id_timestamp_idx ON events.pages (path, session_id, timestamp); + CREATE INDEX IF NOT EXISTS pages_path_pathLNGT2_idx ON events.pages (path) WHERE length(path) > 2; CREATE INDEX IF NOT EXISTS pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; CREATE INDEX IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; diff --git a/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql b/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql index 1c5e0a06f..d11cad5be 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.6.0/1.6.0.sql @@ -53,12 +53,36 @@ CREATE TABLE IF NOT EXISTS dashboard_widgets ); ALTER TABLE events_common.requests - ADD COLUMN IF NOT EXISTS host text NULL, - ADD COLUMN IF NOT EXISTS path text NULL, - ADD COLUMN IF NOT EXISTS query text NULL; + ADD COLUMN IF NOT EXISTS host text NULL, + ADD COLUMN IF NOT EXISTS path text NULL, + ADD COLUMN IF NOT EXISTS query text NULL; ALTER TABLE events.pages ADD COLUMN IF NOT EXISTS query text NULL; + +DO +$$ + BEGIN + IF EXISTS(SELECT * + FROM information_schema.columns + WHERE table_schema = 'events' + AND table_name = 'pages' + AND column_name = 'base_path') + THEN + ALTER TABLE events.pages + DROP COLUMN IF EXISTS path; + ALTER TABLE events.pages + RENAME COLUMN base_path TO path; + DROP INDEX IF EXISTS events.pages_base_path_gin_idx2; + DROP INDEX IF EXISTS pages_base_path_idx2; + ALTER INDEX IF EXISTS events.pages_base_path_gin_idx RENAME TO pages_path_gin_idx; + ALTER INDEX IF EXISTS events.pages_base_path_idx RENAME TO pages_path_idx; + ALTER INDEX IF EXISTS events.pages_base_path_session_id_timestamp_idx RENAME TO pages_path_session_id_timestamp_idx; + ALTER INDEX IF EXISTS events.pages_base_path_base_pathLNGT2_idx RENAME TO pages_path_pathLNGT2_idx; + END IF; + END +$$; + COMMIT; ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; @@ -319,3 +343,6 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS requests_query_nn_gin_idx ON events_comm CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; + +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_path_session_id_timestamp_idx ON events.pages (path, session_id, timestamp); +CREATE INDEX CONCURRENTLY IF NOT EXISTS pages_path_pathLNGT2_idx ON events.pages (path) WHERE length(path) > 2; \ 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 da2c7bc9c..5a01226f1 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -600,7 +600,6 @@ $$ -- --- events_common.sql --- - CREATE SCHEMA IF NOT EXISTS events_common; CREATE TYPE events_common.custom_level AS ENUM ('info','error'); @@ -675,7 +674,6 @@ $$ CREATE INDEX requests_query_nn_gin_idx ON events_common.requests USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; -- --- events.sql --- - CREATE SCHEMA IF NOT EXISTS events; CREATE TABLE events.pages ( @@ -684,7 +682,6 @@ $$ timestamp bigint NOT NULL, host text NOT NULL, path text NOT NULL, - base_path text NOT NULL, query text NULL, referrer text DEFAULT NULL, base_referrer text DEFAULT NULL, @@ -702,13 +699,9 @@ $$ PRIMARY KEY (session_id, message_id) ); CREATE INDEX pages_session_id_idx ON events.pages (session_id); - CREATE INDEX pages_base_path_gin_idx ON events.pages USING GIN (base_path gin_trgm_ops); CREATE INDEX pages_base_referrer_gin_idx ON events.pages USING GIN (base_referrer gin_trgm_ops); CREATE INDEX pages_timestamp_idx ON events.pages (timestamp); CREATE INDEX pages_session_id_timestamp_idx ON events.pages (session_id, timestamp); - CREATE INDEX pages_base_path_gin_idx2 ON events.pages USING GIN (RIGHT(base_path, length(base_path) - 1) gin_trgm_ops); - CREATE INDEX pages_base_path_idx ON events.pages (base_path); - CREATE INDEX pages_base_path_idx2 ON events.pages (RIGHT(base_path, length(base_path) - 1)); CREATE INDEX pages_base_referrer_idx ON events.pages (base_referrer); CREATE INDEX pages_base_referrer_gin_idx2 ON events.pages USING GIN (RIGHT(base_referrer, length(base_referrer) - (CASE @@ -739,10 +732,10 @@ $$ time_to_interactive > 0; CREATE INDEX 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 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 pages_base_path_session_id_timestamp_idx ON events.pages (base_path, session_id, timestamp); - CREATE INDEX pages_base_path_base_pathLNGT2_idx ON events.pages (base_path) WHERE length(base_path) > 2; - CREATE INDEX IF NOT EXISTS pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; - CREATE INDEX IF NOT EXISTS pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; + CREATE INDEX pages_path_session_id_timestamp_idx ON events.pages (path, session_id, timestamp); + CREATE INDEX pages_path_pathLNGT2_idx ON events.pages (path) WHERE length(path) > 2; + CREATE INDEX pages_query_nn_idx ON events.pages (query) WHERE query IS NOT NULL; + CREATE INDEX pages_query_nn_gin_idx ON events.pages USING GIN (query gin_trgm_ops) WHERE query IS NOT NULL; CREATE TABLE events.clicks From b3debda1215825a873794c1febf13fd62e65b402 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 22 Apr 2022 15:22:25 +0200 Subject: [PATCH 269/294] change(ui) - filter series limit to 3, widget drilldown sessions pagination --- .../components/MetricsList/MetricsList.tsx | 6 +++- .../components/WidgetForm/WidgetForm.tsx | 6 ++-- .../WidgetSessions/WidgetSessions.tsx | 31 +++++++++++++------ frontend/app/components/ui/Button/Button.js | 2 +- frontend/app/mstore/metricStore.ts | 6 ++++ 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx index f46d76de0..9d895de13 100644 --- a/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx +++ b/frontend/app/components/Dashboard/components/MetricsList/MetricsList.tsx @@ -1,5 +1,5 @@ import { useObserver } from 'mobx-react-lite'; -import React from 'react'; +import React, { useEffect } from 'react'; import { NoContent, Pagination } from 'UI'; import { useStore } from 'App/mstore'; import { getRE } from 'App/utils'; @@ -22,6 +22,10 @@ function MetricsList(props: Props) { const list: any = metricsSearch !== '' ? filterList(metrics) : metrics; const lenth = list.length; + useEffect(() => { + metricStore.updateKey('sessionsPage', 1); + }, []) + return useObserver(() => (
diff --git a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx index 72c9c33c7..79b44bc5a 100644 --- a/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx +++ b/frontend/app/components/Dashboard/components/WidgetForm/WidgetForm.tsx @@ -29,6 +29,7 @@ function WidgetForm(props: Props) { const isTable = metric.metricType === 'table'; const _issueOptions = [{ text: 'All', value: 'all' }].concat(issueOptions); const canAddToDashboard = metric.exists() && dashboards.length > 0; + const canAddSeries = metric.series.length < 3; const write = ({ target: { value, name } }) => metricStore.merge({ [ name ]: value }); const writeOption = (e, { value, name }) => { @@ -150,16 +151,17 @@ function WidgetForm(props: Props) {
-