diff --git a/components/WebsiteDetails.js b/components/WebsiteDetails.js index 4f901efc..4ebe5a0d 100644 --- a/components/WebsiteDetails.js +++ b/components/WebsiteDetails.js @@ -16,14 +16,17 @@ import BrowsersTable from './metrics/BrowsersTable'; import OSTable from './metrics/OSTable'; import DevicesTable from './metrics/DevicesTable'; import CountriesTable from './metrics/CountriesTable'; +import EventsTable from './metrics/EventsTable'; +import EventsChart from './metrics/EventsChart'; export default function WebsiteDetails({ websiteId, defaultDateRange = '7day' }) { const [data, setData] = useState(); const [chartLoaded, setChartLoaded] = useState(false); const [countryData, setCountryData] = useState(); + const [eventsData, setEventsData] = useState(); const [dateRange, setDateRange] = useState(getDateRange(defaultDateRange)); const [expand, setExpand] = useState(); - const { startDate, endDate } = dateRange; + const { startDate, endDate, unit } = dateRange; const BackButton = () => ( - )} - + {data ? ( + <> +
+
{title}
+ {headerComponent} +
+ {metric} +
+
+
+ {limit + ? rankings.map(row => getRow(row)) + : data?.length > 0 && ( + + {Row} + + )} +
+
+ {limit && data.length > limit && ( + + )} +
+ + ) : ( + + )} ); } -const AnimatedRow = ({ label, value = 0, percent, animate, format, onClick, labelRenderer }) => { +const AnimatedRow = ({ label, value = 0, percent, animate, format, onClick }) => { const props = useSpring({ width: percent, y: value, @@ -125,7 +125,7 @@ const AnimatedRow = ({ label, value = 0, percent, animate, format, onClick, labe return (
-
{labelRenderer(decodeURI(label))}
+
{label}
{props.y?.interpolate(format)}
diff --git a/components/metrics/PagesTable.js b/components/metrics/PagesTable.js index 48ba85c3..aab8b23c 100644 --- a/components/metrics/PagesTable.js +++ b/components/metrics/PagesTable.js @@ -25,6 +25,7 @@ export default function PagesTable({ limit={limit} dataFilter={urlFilter} filterOptions={{ domain: websiteDomain, raw: filter === 'Raw' }} + renderLabel={({ x }) => decodeURI(x)} onExpand={onExpand} /> ); diff --git a/components/metrics/PageviewsChart.js b/components/metrics/PageviewsChart.js index fe8c44e4..f8aa0cdc 100644 --- a/components/metrics/PageviewsChart.js +++ b/components/metrics/PageviewsChart.js @@ -1,67 +1,30 @@ -import React, { useState, useRef, useEffect, useCallback } from 'react'; -import ReactTooltip from 'react-tooltip'; -import classNames from 'classnames'; -import ChartJS from 'chart.js'; -import { format } from 'date-fns'; -import styles from './PageviewsChart.module.css'; +import React from 'react'; +import CheckVisible from 'components/helpers/CheckVisible'; +import BarChart from './BarChart'; -export default function PageviewsChart({ - websiteId, - data, - unit, - animationDuration = 300, - className, - children, -}) { - const canvas = useRef(); - const chart = useRef(); - const [tooltip, setTooltip] = useState({}); +export default function PageviewsChart({ websiteId, data, unit, className }) { + const handleUpdate = chart => { + const { + data: { datasets }, + } = chart; - const renderLabel = useCallback( - (label, index, values) => { - const d = new Date(values[index].value); - const n = data.pageviews.length; + datasets[0].data = data.uniques; + datasets[1].data = data.pageviews; - switch (unit) { - case 'day': - if (n >= 15) { - return index % ~~(n / 15) === 0 ? format(d, 'MMM d') : ''; - } - return format(d, 'EEE M/d'); - case 'month': - return format(d, 'MMMM'); - default: - return label; - } - }, - [unit, data], - ); - - const renderTooltip = model => { - const { opacity, title, body, labelColors } = model; - - if (!opacity) { - setTooltip(null); - } else { - const [label, value] = body[0].lines[0].split(':'); - - setTooltip({ - title: title[0], - value, - label, - labelColor: labelColors[0].backgroundColor, - }); - } + chart.update(); }; - function draw() { - if (!canvas.current) return; + if (!data) { + return null; + } - if (!chart.current) { - chart.current = new ChartJS(canvas.current, { - type: 'bar', - data: { - datasets: [ + return ( + + {visible => ( + { - if (data) { - draw(); - setTooltip(null); - } - }, [data]); - - return ( -
- - - {tooltip ? : null} - - {children} -
+ ]} + unit={unit} + records={data.pageviews.length} + animationDuration={visible ? 300 : 0} + onUpdate={handleUpdate} + /> + )} +
); } - -const Tooltip = ({ title, value, label, labelColor }) => ( -
-
-
{title}
-
-
-
-
- {value} {label} -
-
-
-); diff --git a/components/metrics/ReferrersTable.js b/components/metrics/ReferrersTable.js index 480a299e..abaa1208 100644 --- a/components/metrics/ReferrersTable.js +++ b/components/metrics/ReferrersTable.js @@ -13,13 +13,13 @@ export default function Referrers({ }) { const [filter, setFilter] = useState('Combined'); - const renderLink = url => { + const renderLink = ({ x: url }) => { return url.startsWith('http') ? ( - {url} + {decodeURI(url)} ) : ( - url + decodeURI(url) ); }; @@ -40,7 +40,7 @@ export default function Referrers({ raw: filter === 'Raw', }} onExpand={onExpand} - labelRenderer={renderLink} + renderLabel={renderLink} /> ); } diff --git a/components/metrics/WebsiteChart.js b/components/metrics/WebsiteChart.js index 4f066808..9c53a560 100644 --- a/components/metrics/WebsiteChart.js +++ b/components/metrics/WebsiteChart.js @@ -1,7 +1,6 @@ -import React, { useState, useEffect, useMemo, useRef } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import classNames from 'classnames'; import PageviewsChart from './PageviewsChart'; -import CheckVisible from '../helpers/CheckVisible'; import MetricsBar from './MetricsBar'; import QuickButtons from './QuickButtons'; import DateFilter from '../common/DateFilter'; @@ -74,18 +73,10 @@ export default function WebsiteChart({
- - {visible => ( - - - - )} - +
+ + +
); diff --git a/lib/date.js b/lib/date.js index f53521b9..80980e27 100644 --- a/lib/date.js +++ b/lib/date.js @@ -98,6 +98,12 @@ export function getDateArray(data, startDate, endDate, unit) { function findData(t) { const x = data.find(e => { + console.log( + new Date(e.t), + getLocalTime(new Date(e.t)), + getLocalTime(new Date(e.t)).getTime(), + normalize(new Date(t)).getTime(), + ); return getLocalTime(new Date(e.t)).getTime() === normalize(new Date(t)).getTime(); }); @@ -108,8 +114,13 @@ export function getDateArray(data, startDate, endDate, unit) { const t = add(startDate, i); const y = findData(t); - arr.push({ t, y }); + arr.push({ ...data[i], t, y }); } return arr; } + +export function getDateLength(startDate, endDate, unit) { + const [diff] = dateFuncs[unit]; + return diff(endDate, startDate) + 1; +} diff --git a/lib/db.js b/lib/db.js index e6d69fdb..d32b9f27 100644 --- a/lib/db.js +++ b/lib/db.js @@ -35,5 +35,6 @@ export default prisma; export async function runQuery(query) { return query.catch(e => { console.error(e); + throw e; }); } diff --git a/lib/filters.js b/lib/filters.js index 6c5a0294..09f27e70 100644 --- a/lib/filters.js +++ b/lib/filters.js @@ -49,12 +49,13 @@ export const urlFilter = (data, { domain, raw }) => { }; export const refFilter = (data, { domain, domainOnly, raw }) => { + const regex = new RegExp(domain.startsWith('http') ? domain : `http[s]?://${domain}`); + const isValidRef = ref => { return ref !== '' && !ref.startsWith('/') && !ref.startsWith('#'); }; if (raw) { - const regex = new RegExp(`http[s]?://([^.]+.)?${domain}`); return data.filter(({ x }) => isValidRef(x) && !regex.test(x)); } @@ -62,7 +63,7 @@ export const refFilter = (data, { domain, domainOnly, raw }) => { try { const { hostname, origin, pathname, searchParams, protocol } = new URL(url); - if (hostname === domain) { + if (hostname === domain || regex.test(url)) { return null; } @@ -124,5 +125,5 @@ export const countryFilter = data => export const percentFilter = data => { const total = data.reduce((n, { y }) => n + y, 0); - return data.map(({ x, y }) => ({ x, y, z: total ? (y / total) * 100 : 0 })); + return data.map(({ x, y, ...props }) => ({ x, y, z: total ? (y / total) * 100 : 0, ...props })); }; diff --git a/lib/queries.js b/lib/queries.js index 3bfc27fe..7c8a9875 100644 --- a/lib/queries.js +++ b/lib/queries.js @@ -5,10 +5,28 @@ import { subMinutes } from 'date-fns'; const POSTGRESQL = 'postgresql'; const MYSQL = 'mysql'; +const DATE_FORMATS = { + minute: '%Y-%m-%d %H:%i:00', + hour: '%Y-%m-%d %H:00:00', + day: '%Y-%m-%d', + month: '%Y-%m-01', + year: '%Y-01-01', +}; + export function getDatabase() { return process.env.DATABASE_TYPE || process.env.DATABASE_URL.split(':')[0]; } +export function getDateQuery(field, unit, timezone) { + if (timezone) { + const tz = moment.tz(timezone).format('Z'); + + return `DATE_FORMAT(convert_tz(${field},'+00:00','${tz}'), '${DATE_FORMATS[unit]}')`; + } + + return `DATE_FORMAT(${field}, '${DATE_FORMATS[unit]}')`; +} + export async function getWebsiteById(website_id) { return runQuery( prisma.website.findOne({ @@ -236,7 +254,7 @@ export function getMetrics(website_id, start_at, end_at) { ` select sum(t.c) as "pageviews", count(distinct t.session_id) as "uniques", - sum(case when t.c = 1 then t.c else 0 end) as "bounces", + sum(case when t.c = 1 then 1 else 0 end) as "bounces", sum(t.time) as "totaltime" from ( select session_id, @@ -260,11 +278,11 @@ export function getMetrics(website_id, start_at, end_at) { ` select sum(t.c) as "pageviews", count(distinct t.session_id) as "uniques", - sum(case when t.c = 1 then t.c else 0 end) as "bounces", + sum(case when t.c = 1 then 1 else 0 end) as "bounces", sum(t.time) as "totaltime" from ( select session_id, - date_trunc('hour', created_at), + ${getDateQuery('created_at', 'hour')}, count(*) c, floor(unix_timestamp(max(created_at)) - unix_timestamp(min(created_at))) as "time" from pageview @@ -296,7 +314,7 @@ export function getPageviews( return prisma.$queryRaw( ` select date_trunc('${unit}', created_at at time zone '${timezone}') t, - count(${count}) y + count(${count}) y from pageview where website_id=$1 and created_at between $2 and $3 @@ -310,11 +328,10 @@ export function getPageviews( } if (db === MYSQL) { - const tz = moment.tz(timezone).format('Z'); return prisma.$queryRaw( ` - select date_trunc('${unit}', convert_tz(created_at,'+00:00','${tz}')) t, - count(${count}) y + select ${getDateQuery('created_at', unit, timezone)} t, + count(${count}) y from pageview where website_id=? and created_at between ? and ? @@ -400,3 +417,47 @@ export function getActiveVisitors(website_id) { return Promise.resolve([]); } + +export function getEvents(website_id, start_at, end_at, timezone = 'utc', unit = 'day') { + const db = getDatabase(); + + if (db === POSTGRESQL) { + return prisma.$queryRaw( + ` + select + event_value x, + date_trunc('${unit}', created_at at time zone '${timezone}') t, + count(*) y + from event + where website_id=$1 + and created_at between $2 and $3 + group by 1, 2 + order by 2 + `, + website_id, + start_at, + end_at, + ); + } + + if (db === MYSQL) { + return prisma.$queryRaw( + ` + select + event_value x, + ${getDateQuery('created_at', unit, timezone)} t, + count(*) y + from event + where website_id=? + and created_at between ? and ? + group by 1, 2 + order by 2 + `, + website_id, + start_at, + end_at, + ); + } + + return Promise.resolve([]); +} diff --git a/package.json b/package.json index f7821a63..b9c7a59e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "0.16.3", + "version": "0.17.0", "description": "A simple, fast, website analytics alternative to Google Analytics. ", "author": "Mike Cao ", "license": "MIT", @@ -45,7 +45,7 @@ "classnames": "^2.2.6", "cookie": "^0.4.1", "cors": "^2.8.5", - "date-fns": "^2.15.0", + "date-fns": "^2.16.0", "date-fns-tz": "^1.0.10", "detect-browser": "^5.1.1", "dotenv": "^8.2.0", @@ -62,7 +62,7 @@ "react-redux": "^7.2.1", "react-simple-maps": "^2.1.2", "react-spring": "^8.0.27", - "react-tooltip": "^4.2.8", + "react-tooltip": "^4.2.9", "react-window": "^1.8.5", "redux": "^4.0.5", "redux-thunk": "^2.3.0", @@ -86,14 +86,14 @@ "eslint-plugin-react": "^7.20.6", "eslint-plugin-react-hooks": "^4.1.0", "husky": "^4.2.5", - "lint-staged": "^10.2.9", + "lint-staged": "^10.2.13", "npm-run-all": "^4.1.5", "postcss-flexbugs-fixes": "^4.2.1", "postcss-import": "^12.0.1", "postcss-preset-env": "^6.7.0", - "prettier": "^2.0.5", + "prettier": "^2.1.1", "prettier-eslint": "^11.0.0", - "rollup": "^2.26.5", + "rollup": "^2.26.6", "rollup-plugin-hashbang": "^2.2.2", "rollup-plugin-terser": "^7.0.0", "stylelint": "^13.6.0", diff --git a/pages/api/website/[id]/events.js b/pages/api/website/[id]/events.js new file mode 100644 index 00000000..d7ead27c --- /dev/null +++ b/pages/api/website/[id]/events.js @@ -0,0 +1,21 @@ +import moment from 'moment-timezone'; +import { getEvents } from 'lib/queries'; +import { ok, badRequest } from 'lib/response'; + +const unitTypes = ['month', 'hour', 'day']; + +export default async (req, res) => { + const { id, start_at, end_at, unit, tz } = req.query; + + if (!moment.tz.zone(tz) || !unitTypes.includes(unit)) { + return badRequest(res); + } + + const websiteId = +id; + const startDate = new Date(+start_at); + const endDate = new Date(+end_at); + + const events = await getEvents(websiteId, startDate, endDate, tz, unit); + + return ok(res, events); +}; diff --git a/pages/api/website/[id]/pageviews.js b/pages/api/website/[id]/pageviews.js index 5e07c0ab..c90e1c6b 100644 --- a/pages/api/website/[id]/pageviews.js +++ b/pages/api/website/[id]/pageviews.js @@ -11,12 +11,13 @@ export default async (req, res) => { return badRequest(res); } - const start = new Date(+start_at); - const end = new Date(+end_at); + const websiteId = +id; + const startDate = new Date(+start_at); + const endDate = new Date(+end_at); const [pageviews, uniques] = await Promise.all([ - getPageviews(+id, start, end, tz, unit, '*'), - getPageviews(+id, start, end, tz, unit, 'distinct session_id'), + getPageviews(websiteId, startDate, endDate, tz, unit, '*'), + getPageviews(websiteId, startDate, endDate, tz, unit, 'distinct session_id'), ]); return ok(res, { pageviews, uniques }); diff --git a/pages/api/website/[id]/rankings.js b/pages/api/website/[id]/rankings.js index a930bca9..8d1d95bf 100644 --- a/pages/api/website/[id]/rankings.js +++ b/pages/api/website/[id]/rankings.js @@ -4,16 +4,42 @@ import { ok, badRequest } from 'lib/response'; const sessionColumns = ['browser', 'os', 'device', 'country']; const pageviewColumns = ['url', 'referrer']; +function getTable(type) { + if (type === 'event') { + return 'event'; + } + + if (sessionColumns.includes(type)) { + return 'session'; + } + + return 'pageview'; +} + +function getColumn(type) { + if (type === 'event') { + return `concat(event_type, ':', event_value)`; + } + return type; +} + export default async (req, res) => { const { id, type, start_at, end_at } = req.query; + const websiteId = +id; + const startDate = new Date(+start_at); + const endDate = new Date(+end_at); - if (!sessionColumns.includes(type) && !pageviewColumns.includes(type)) { + if (type !== 'event' && !sessionColumns.includes(type) && !pageviewColumns.includes(type)) { return badRequest(res); } - const table = sessionColumns.includes(type) ? 'session' : 'pageview'; - - const rankings = await getRankings(+id, new Date(+start_at), new Date(+end_at), type, table); + const rankings = await getRankings( + websiteId, + startDate, + endDate, + getColumn(type), + getTable(type), + ); return ok(res, rankings); }; diff --git a/sql/schema.mysql.sql b/sql/schema.mysql.sql index 61a16509..a4fd87f0 100644 --- a/sql/schema.mysql.sql +++ b/sql/schema.mysql.sql @@ -3,7 +3,6 @@ drop table if exists pageview; drop table if exists session; drop table if exists website; drop table if exists account; -drop function if exists date_trunc; create table account ( user_id int unsigned not null auto_increment primary key, @@ -71,37 +70,11 @@ create index session_website_id_idx on session(website_id); create index pageview_created_at_idx on pageview(created_at); create index pageview_website_id_idx on pageview(website_id); create index pageview_session_id_idx on pageview(session_id); +create index pageview_website_id_created_at_idx on pageview(website_id, created_at); +create index pageview_website_id_session_id_created_at_idx on pageview(website_id, session_id, created_at); create index event_created_at_idx on event(created_at); create index event_website_id_idx on event(website_id); create index event_session_id_idx on event(session_id); -delimiter $$ - -create function date_trunc( - in_granularity enum('minute', 'hour', 'day', 'month', 'year'), - in_datetime datetime(6) -) -returns datetime(6) -deterministic -begin - if (in_granularity = 'minute') then - return DATE_FORMAT(in_datetime, '%Y-%m-%d %H:%i:00.0000'); - end if; - if (in_granularity = 'hour') then - return DATE_FORMAT(in_datetime, '%Y-%m-%d %H:00:00.0000'); - end if; - if (in_granularity = 'day') then - return DATE_FORMAT(in_datetime, '%Y-%m-%d 00:00:00.0000'); - end if; - if (in_granularity = 'month') then - return DATE_FORMAT(in_datetime, '%Y-%m-01 00:00:00.0000'); - end if; - if (in_granularity = 'year') then - return DATE_FORMAT(in_datetime, '%Y-01-01 00:00:00.0000'); - end if; -end; - -$$ - insert into account (username, password, is_admin) values ('admin', '$2b$10$BUli0c.muyCW1ErNJc3jL.vFRFtFJWrT8/GcR4A.sUdCznaXiqFXa', true); \ No newline at end of file diff --git a/sql/schema.postgresql.sql b/sql/schema.postgresql.sql index 5206257d..28297c4a 100644 --- a/sql/schema.postgresql.sql +++ b/sql/schema.postgresql.sql @@ -64,6 +64,8 @@ create index session_website_id_idx on session(website_id); create index pageview_created_at_idx on pageview(created_at); create index pageview_website_id_idx on pageview(website_id); create index pageview_session_id_idx on pageview(session_id); +create index pageview_website_id_created_at_idx on pageview(website_id, created_at); +create index pageview_website_id_session_id_created_at_idx on pageview(website_id, session_id, created_at); create index event_created_at_idx on event(created_at); create index event_website_id_idx on event(website_id); diff --git a/yarn.lock b/yarn.lock index 8215dc0e..588dd177 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1086,7 +1086,7 @@ levenary "^1.1.1" semver "^5.5.0" -"@babel/preset-modules@0.1.3", "@babel/preset-modules@^0.1.3": +"@babel/preset-modules@0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72" integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg== @@ -1097,6 +1097,17 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" +"@babel/preset-modules@^0.1.3": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" + integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== + 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" + "@babel/preset-react@7.9.4": version "7.9.4" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.9.4.tgz#c6c97693ac65b6b9c0b4f25b948a8f665463014d" @@ -1458,9 +1469,9 @@ integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= "@types/node@*": - version "14.6.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.0.tgz#7d4411bf5157339337d7cff864d9ff45f177b499" - integrity sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA== + version "14.6.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.1.tgz#fdf6f6c6c73d3d8eee9c98a9a0485bc524b048d7" + integrity sha512-HnYlg/BRF8uC1FyKRFZwRaCPTPYKa+6I8QiUZFLredaGOou481cgFS4wKRFyKvQtX8xudqkSdBczJHIYSQYKrQ== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1489,40 +1500,40 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== -"@typescript-eslint/experimental-utils@3.9.1": - version "3.9.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.9.1.tgz#b140b2dc7a7554a44f8a86fb6fe7cbfe57ca059e" - integrity sha512-lkiZ8iBBaYoyEKhCkkw4SAeatXyBq9Ece5bZXdLe1LWBUwTszGbmbiqmQbwWA8cSYDnjWXp9eDbXpf9Sn0hLAg== +"@typescript-eslint/experimental-utils@3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686" + integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/types" "3.9.1" - "@typescript-eslint/typescript-estree" "3.9.1" + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/typescript-estree" "3.10.1" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^3.0.0": - version "3.9.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.9.1.tgz#ab7983abaea0ae138ff5671c7c7739d8a191b181" - integrity sha512-y5QvPFUn4Vl4qM40lI+pNWhTcOWtpZAJ8pOEQ21fTTW4xTJkRplMjMRje7LYTXqVKKX9GJhcyweMz2+W1J5bMg== + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467" + integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "3.9.1" - "@typescript-eslint/types" "3.9.1" - "@typescript-eslint/typescript-estree" "3.9.1" + "@typescript-eslint/experimental-utils" "3.10.1" + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/typescript-estree" "3.10.1" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/types@3.9.1": - version "3.9.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.9.1.tgz#b2a6eaac843cf2f2777b3f2464fb1fbce5111416" - integrity sha512-15JcTlNQE1BsYy5NBhctnEhEoctjXOjOK+Q+rk8ugC+WXU9rAcS2BYhoh6X4rOaXJEpIYDl+p7ix+A5U0BqPTw== +"@typescript-eslint/types@3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" + integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== -"@typescript-eslint/typescript-estree@3.9.1": - version "3.9.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.9.1.tgz#fd81cada74bc8a7f3a2345b00897acb087935779" - integrity sha512-IqM0gfGxOmIKPhiHW/iyAEXwSVqMmR2wJ9uXHNdFpqVvPaQ3dWg302vW127sBpAiqM9SfHhyS40NKLsoMpN2KA== +"@typescript-eslint/typescript-estree@3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" + integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w== dependencies: - "@typescript-eslint/types" "3.9.1" - "@typescript-eslint/visitor-keys" "3.9.1" + "@typescript-eslint/types" "3.10.1" + "@typescript-eslint/visitor-keys" "3.10.1" debug "^4.1.1" glob "^7.1.6" is-glob "^4.0.1" @@ -1530,10 +1541,10 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@3.9.1": - version "3.9.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.9.1.tgz#92af3747cdb71509199a8f7a4f00b41d636551d1" - integrity sha512-zxdtUjeoSh+prCpogswMwVUJfEFmCOjdzK9rpNjNBfm6EyPt99x3RrJoBOGZO23FCt0WPKUCOL5mb/9D5LjdwQ== +"@typescript-eslint/visitor-keys@3.10.1": + version "3.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" + integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ== dependencies: eslint-visitor-keys "^1.1.0" @@ -2352,9 +2363,9 @@ camelcase@^6.0.0: integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001093, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001111: - version "1.0.30001117" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001117.tgz#69a9fae5d480eaa9589f7641a83842ad396d17c4" - integrity sha512-4tY0Fatzdx59kYjQs+bNxUwZB03ZEBgVmJ1UkFPz/Q8OLiUUbjct2EdpnXj0fvFTPej2EkbPIG0w8BWsjAyk1Q== + version "1.0.30001119" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001119.tgz#99185d04bc00e76a86c9ff731dc5ec8e53aefca1" + integrity sha512-Hpwa4obv7EGP+TjkCh/wVvbtNJewxmtg4yVJBLFnxo35vbPapBr138bUWENkb5j5L9JZJ9RXLn4OrXRG/cecPQ== ccount@^1.0.0: version "1.0.5" @@ -2536,7 +2547,7 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-truncate@2.1.0, cli-truncate@^2.1.0: +cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== @@ -2637,10 +2648,10 @@ commander@2, commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== +commander@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.1.0.tgz#f8d722b78103141006b66f4c7ba1e97315ba75bc" + integrity sha512-wl7PNrYWd2y5mp1OK/LhTlv8Ff4kQJQRXXAvF+uU/TPNiVJUxZLRYGj/B0y/lPGAVcSbJqH2Za/cvHmrPMC8mA== common-tags@^1.4.0: version "1.8.0" @@ -2765,6 +2776,17 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + 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" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -3092,10 +3114,10 @@ date-fns-tz@^1.0.10: resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-1.0.10.tgz#30fef0038f80534fddd8e133a6b8ca55ba313748" integrity sha512-cHQAz0/9uDABaUNDM80Mj1FL4ODlxs1xEY4b0DQuAooO2UdNKvDkNbV8ogLnxLbv02Ru1HXFcot0pVvDRBgptg== -date-fns@^2.15.0: - version "2.15.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.15.0.tgz#424de6b3778e4e69d3ff27046ec136af58ae5d5f" - integrity sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ== +date-fns@^2.16.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.0.tgz#d34f0f5f2fd498c984513042e8f7247ea86c4cb7" + integrity sha512-DWTRyfOA85sZ4IiXPHhiRIOs3fW5U6Msrp+gElXARa6EpoQTXPyHQmh7hr+ssw2nx9FtOQWnAMJKgL5vaJqILw== debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" @@ -3351,9 +3373,9 @@ duplexify@^3.4.2, duplexify@^3.6.0: stream-shift "^1.0.0" electron-to-chromium@^1.3.488, electron-to-chromium@^1.3.523: - version "1.3.544" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.544.tgz#ac1f7d319f6060f3d6d122261d542ec77eb1427e" - integrity sha512-jx6H7M1db76Q/dI3MadZC4qwNTvpiq8tdYEJswxexrIm5bH+LKRdg+VAteMF1tJJbBLrcuogE9N3nxT3Dp1gag== + version "1.3.553" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.553.tgz#c4693d8660470a3aa830907890e446a9f3b26376" + integrity sha512-wi/hoMuTGK6OJoLOHqmXFA9BWOQGF2nInCfk+/Owhd4VVfuenKE2LZr9TtFCmwyda2SE9hG+sRnqRCwhYgFeIg== elliptic@^6.5.3: version "6.5.3" @@ -3404,7 +3426,7 @@ enhanced-resolve@^4.3.0: memory-fs "^0.5.0" tapable "^1.0.0" -enquirer@^2.3.5: +enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -3746,7 +3768,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -execa@^4.0.1: +execa@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== @@ -4435,11 +4457,11 @@ ignore@^5.1.4, ignore@^5.1.8: integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== immer@^7.0.3: - version "7.0.7" - resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.7.tgz#9dfe713d49bf871cc59aedfce59b1992fa37a977" - integrity sha512-Q8yYwVADJXrNfp1ZUAh4XDHkcoE3wpdpb4mC5abDSajs2EbW8+cGdPyAnglMyLnm7EF6ojD2xBFX7L5i4TIytw== + version "7.0.8" + resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.8.tgz#41dcbc5669a76500d017bef3ad0d03ce0a1d7c1e" + integrity sha512-XnpIN8PXBBaOD43U8Z17qg6RQiKQYGDGGCIbz1ixmLGwBkSWwmrmx5X7d+hTtXDM8ur7m5OdLE0PiO+y5RB3pw== -import-fresh@^3.0.0, import-fresh@^3.1.0: +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -4999,20 +5021,20 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -lint-staged@^10.2.9: - version "10.2.11" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.11.tgz#713c80877f2dc8b609b05bc59020234e766c9720" - integrity sha512-LRRrSogzbixYaZItE2APaS4l2eJMjjf5MbclRZpLJtcQJShcvUzKXsNeZgsLIZ0H0+fg2tL4B59fU9wHIHtFIA== +lint-staged@^10.2.13: + version "10.2.13" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.13.tgz#b9c504683470edfc464b7d3fe3845a5a1efcd814" + integrity sha512-conwlukNV6aL9SiMWjFtDp5exeDnTMekdNPDZsKGnpfQuHcO0E3L3Bbf58lcR+M7vk6LpCilxDAVks/DDVBYlA== dependencies: - chalk "^4.0.0" - cli-truncate "2.1.0" - commander "^5.1.0" - cosmiconfig "^6.0.0" + chalk "^4.1.0" + cli-truncate "^2.1.0" + commander "^6.0.0" + cosmiconfig "^7.0.0" debug "^4.1.1" dedent "^0.7.0" - enquirer "^2.3.5" - execa "^4.0.1" - listr2 "^2.1.0" + enquirer "^2.3.6" + execa "^4.0.3" + listr2 "^2.6.0" log-symbols "^4.0.0" micromatch "^4.0.2" normalize-path "^3.0.0" @@ -5020,10 +5042,10 @@ lint-staged@^10.2.9: string-argv "0.3.1" stringify-object "^3.3.0" -listr2@^2.1.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.6.0.tgz#788a3d202978a1b8582062952cbc49272c8e206a" - integrity sha512-nwmqTJYQQ+AsKb4fCXH/6/UmLCEDL1jkRAdSn9M6cEUzoRGrs33YD/3N86gAZQnGZ6hxV18XSdlBcJ1GTmetJA== +listr2@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.6.1.tgz#fbbabd8eea723924df7530042c1990b346e81706" + integrity sha512-1aPX9GkS+W0aHfPUDedJqeqj0DOe1605NaNoqdwEYw/UF2UbZgCIIMpXXZALeG/8xzwMBztguzQEubU5Xw1Qbw== dependencies: chalk "^4.1.0" cli-truncate "^2.1.0" @@ -5164,9 +5186,9 @@ loglevel-colored-level-prefix@^1.0.0: loglevel "^1.4.1" loglevel@^1.4.1: - version "1.6.8" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" - integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" + integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== longest-streak@^2.0.1: version "2.0.4" @@ -6779,10 +6801,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.0.0, prettier@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" - integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== +prettier@^2.0.0, prettier@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.1.tgz#d9485dd5e499daa6cb547023b87a6cf51bee37d6" + integrity sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw== pretty-format@^23.0.1: version "23.6.0" @@ -6999,10 +7021,10 @@ react-spring@^8.0.27: "@babel/runtime" "^7.3.1" prop-types "^15.5.8" -react-tooltip@^4.2.8: - version "4.2.8" - resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.8.tgz#270858fee46fab73b66de316271aa94145f7446b" - integrity sha512-pDWa0/khTAgIfldp95tHgyuYyBhWNlfaU2LF9ubAKxpoqNe15uyf+uLlnhK/Lstb6FU8E8/SL28Wp6oEO9xw3g== +react-tooltip@^4.2.9: + version "4.2.9" + resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.9.tgz#0dd08d14191f5d0e56b51c822fa20c2d81a24272" + integrity sha512-DgZyg5oxk9/orgePDLLeuDtlwwYv7CalJRahk9nNsoEJDzIO58GC6zSAet4bKTm6c01hg1z3EocP9H0nmMHTMA== dependencies: prop-types "^15.7.2" uuid "^7.0.3" @@ -7417,10 +7439,10 @@ rollup-plugin-terser@^7.0.0: serialize-javascript "^4.0.0" terser "^5.0.0" -rollup@^2.26.5: - version "2.26.5" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.26.5.tgz#5562ec36fcba3eed65cfd630bd78e037ad0e0307" - integrity sha512-rCyFG3ZtQdnn9YwfuAVH0l/Om34BdO5lwCA0W6Hq+bNB21dVEBbCRxhaHOmu1G7OBFDWytbzAC104u7rxHwGjA== +rollup@^2.26.6: + version "2.26.6" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.26.6.tgz#0b460c1da224c6af12a1e948a28c513aa11f2b93" + integrity sha512-iSB7eE3k/VNQHnI7ckS++4yIqTamoUCB1xo7MswhJ/fg22oFYR5+xCrUZVviBj97jvc5A31MPbVMw1Wc3jWxmw== optionalDependencies: fsevents "~2.1.2" @@ -8986,7 +9008,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.7.2: +yaml@^1.10.0, yaml@^1.7.2: version "1.10.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==